// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections;
using System.Runtime.CompilerServices;
using Microsoft.EntityFrameworkCore.Query.Internal;

// ReSharper disable once CheckNamespace
namespace Microsoft.EntityFrameworkCore;

/// <summary>
///     Entity Framework LINQ related extension methods.
/// </summary>
[UnconditionalSuppressMessage(
     "ReflectionAnalysis",
     "IL2060",
     Justification =
         "MakeGenericMethod is used in this class to create MethodCallExpression nodes, but only if the method in question is called "
         + "from user code - so it's never trimmed. After https://github.com/dotnet/linker/issues/2482 is fixed, the suppression will no "
         + "longer be necessary."), UnconditionalSuppressMessage(
     "AOT",
     "IL3050:Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling.",
     Justification =
         "MakeGenericMethod is used in this class to create MethodCallExpression nodes, but only if the method in question is called "
         + "from user code - so it's never trimmed.")]
public static class EntityFrameworkQueryableExtensions
{
    /// <summary>
    ///     Generates a string representation of the query used. This string may not be suitable for direct execution and is intended only
    ///     for use in debugging.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         This method is only typically supported by queries generated by Entity Framework Core.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-query-string">Viewing SQL generated by EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <param name="source">The query source.</param>
    /// <returns>The query string for debugging.</returns>
    /// <exception cref="ArgumentNullException"><paramref name="source" /> is <see langword="null" />.</exception>
    public static string ToQueryString(this IQueryable source)
        => source.Provider.Execute<IEnumerable>(source.Expression) is IQueryingEnumerable queryingEnumerable
            ? queryingEnumerable.ToQueryString()
            : CoreStrings.NotQueryingEnumerable;

    #region Any/All

    /// <summary>
    ///     Asynchronously determines whether a sequence contains any elements.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <typeparam name="TSource">The type of the elements of <paramref name="source" />.</typeparam>
    /// <param name="source">An <see cref="IQueryable{T}" /> to check for being empty.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains <see langword="true" /> if the source sequence contains any elements; otherwise, <see langword="false" />.
    /// </returns>
    /// <exception cref="ArgumentNullException"><paramref name="source" /> is <see langword="null" />.</exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static Task<bool> AnyAsync<TSource>(
        this IQueryable<TSource> source,
        CancellationToken cancellationToken = default)
        => ExecuteAsync<TSource, Task<bool>>(QueryableMethods.AnyWithoutPredicate, source, cancellationToken);

    /// <summary>
    ///     Asynchronously determines whether any element of a sequence satisfies a condition.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <typeparam name="TSource">The type of the elements of <paramref name="source" />.</typeparam>
    /// <param name="source">An <see cref="IQueryable{T}" /> whose elements to test for a condition.</param>
    /// <param name="predicate">A function to test each element for a condition.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains <see langword="true" /> if any elements in the source sequence pass the test in the specified
    ///     predicate; otherwise, <see langword="false" />.
    /// </returns>
    /// <exception cref="ArgumentNullException">
    ///     <paramref name="source" /> or <paramref name="predicate" /> is <see langword="null" />.
    /// </exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static Task<bool> AnyAsync<TSource>(
        this IQueryable<TSource> source,
        Expression<Func<TSource, bool>> predicate,
        CancellationToken cancellationToken = default)
    {
        Check.NotNull(predicate);

        return ExecuteAsync<TSource, Task<bool>>(QueryableMethods.AnyWithPredicate, source, predicate, cancellationToken);
    }

    /// <summary>
    ///     Asynchronously determines whether all the elements of a sequence satisfy a condition.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <typeparam name="TSource">The type of the elements of <paramref name="source" />.</typeparam>
    /// <param name="source">An <see cref="IQueryable{T}" /> whose elements to test for a condition.</param>
    /// <param name="predicate">A function to test each element for a condition.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains <see langword="true" /> if every element of the source sequence passes the test in the specified
    ///     predicate; otherwise, <see langword="false" />.
    /// </returns>
    /// <exception cref="ArgumentNullException">
    ///     <paramref name="source" /> or <paramref name="predicate" /> is <see langword="null" />.
    /// </exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static Task<bool> AllAsync<TSource>(
        this IQueryable<TSource> source,
        Expression<Func<TSource, bool>> predicate,
        CancellationToken cancellationToken = default)
    {
        Check.NotNull(predicate);

        return ExecuteAsync<TSource, Task<bool>>(QueryableMethods.All, source, predicate, cancellationToken);
    }

    #endregion

    #region Count/LongCount

    /// <summary>
    ///     Asynchronously returns the number of elements in a sequence.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <typeparam name="TSource">The type of the elements of <paramref name="source" />.</typeparam>
    /// <param name="source">An <see cref="IQueryable{T}" /> that contains the elements to be counted.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains the number of elements in the input sequence.
    /// </returns>
    /// <exception cref="ArgumentNullException"><paramref name="source" /> is <see langword="null" />.</exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static Task<int> CountAsync<TSource>(
        this IQueryable<TSource> source,
        CancellationToken cancellationToken = default)
        => ExecuteAsync<TSource, Task<int>>(QueryableMethods.CountWithoutPredicate, source, cancellationToken);

    /// <summary>
    ///     Asynchronously returns the number of elements in a sequence that satisfy a condition.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <typeparam name="TSource">The type of the elements of <paramref name="source" />.</typeparam>
    /// <param name="source">An <see cref="IQueryable{T}" /> that contains the elements to be counted.</param>
    /// <param name="predicate">A function to test each element for a condition.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains the number of elements in the sequence that satisfy the condition in the predicate
    ///     function.
    /// </returns>
    /// <exception cref="ArgumentNullException">
    ///     <paramref name="source" /> or <paramref name="predicate" /> is <see langword="null" />.
    /// </exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static Task<int> CountAsync<TSource>(
        this IQueryable<TSource> source,
        Expression<Func<TSource, bool>> predicate,
        CancellationToken cancellationToken = default)
    {
        Check.NotNull(predicate);

        return ExecuteAsync<TSource, Task<int>>(QueryableMethods.CountWithPredicate, source, predicate, cancellationToken);
    }

    /// <summary>
    ///     Asynchronously returns a <see cref="long" /> that represents the total number of elements in a sequence.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <typeparam name="TSource">The type of the elements of <paramref name="source" />.</typeparam>
    /// <param name="source">An <see cref="IQueryable{T}" /> that contains the elements to be counted.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains the number of elements in the input sequence.
    /// </returns>
    /// <exception cref="ArgumentNullException"><paramref name="source" /> is <see langword="null" />.</exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static Task<long> LongCountAsync<TSource>(
        this IQueryable<TSource> source,
        CancellationToken cancellationToken = default)
        => ExecuteAsync<TSource, Task<long>>(QueryableMethods.LongCountWithoutPredicate, source, cancellationToken);

    /// <summary>
    ///     Asynchronously returns a <see cref="long" /> that represents the number of elements in a sequence
    ///     that satisfy a condition.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <typeparam name="TSource">The type of the elements of <paramref name="source" />.</typeparam>
    /// <param name="source">An <see cref="IQueryable{T}" /> that contains the elements to be counted.</param>
    /// <param name="predicate">A function to test each element for a condition.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains the number of elements in the sequence that satisfy the condition in the predicate
    ///     function.
    /// </returns>
    /// <exception cref="ArgumentNullException">
    ///     <paramref name="source" /> or <paramref name="predicate" /> is <see langword="null" />.
    /// </exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static Task<long> LongCountAsync<TSource>(
        this IQueryable<TSource> source,
        Expression<Func<TSource, bool>> predicate,
        CancellationToken cancellationToken = default)
    {
        Check.NotNull(predicate);

        return ExecuteAsync<TSource, Task<long>>(QueryableMethods.LongCountWithPredicate, source, predicate, cancellationToken);
    }

    #endregion

    #region ElementAt/ElementAtOrDefault

    /// <summary>
    ///     Asynchronously returns the element at a specified index in a sequence.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <typeparam name="TSource">The type of the elements of <paramref name="source" />.</typeparam>
    /// <param name="source">An <see cref="IQueryable{T}" /> to return the element from.</param>
    /// <param name="index">The zero-based index of the element to retrieve.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains the element at a specified index in a <paramref name="source" /> sequence.
    /// </returns>
    /// <exception cref="ArgumentNullException">
    ///     <paramref name="source" /> is <see langword="null" />.
    /// </exception>
    /// <exception cref="ArgumentOutOfRangeException">
    ///     <para>
    ///         <paramref name="index" /> is less than zero.
    ///     </para>
    /// </exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static Task<TSource> ElementAtAsync<TSource>(
        this IQueryable<TSource> source,
        int index,
        CancellationToken cancellationToken = default)
    {
        Check.NotNull(index);

        return ExecuteAsync<TSource, Task<TSource>>(
            QueryableMethods.ElementAt, source, Expression.Constant(index), cancellationToken);
    }

    /// <summary>
    ///     Asynchronously returns the element at a specified index in a sequence, or a default value if the index is out of range.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <typeparam name="TSource">The type of the elements of <paramref name="source" />.</typeparam>
    /// <param name="source">An <see cref="IQueryable{T}" /> to return the element from.</param>
    /// <param name="index">The zero-based index of the element to retrieve.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains the element at a specified index in a <paramref name="source" /> sequence.
    /// </returns>
    /// <exception cref="ArgumentNullException">
    ///     <paramref name="source" /> is <see langword="null" />.
    /// </exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static Task<TSource?> ElementAtOrDefaultAsync<TSource>(
        this IQueryable<TSource> source,
        int index,
        CancellationToken cancellationToken = default)
    {
        Check.NotNull(index);

        return ExecuteAsync<TSource, Task<TSource?>>(
            QueryableMethods.ElementAtOrDefault, source, Expression.Constant(index), cancellationToken);
    }

    #endregion

    #region First/FirstOrDefault

    /// <summary>
    ///     Asynchronously returns the first element of a sequence.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <typeparam name="TSource">The type of the elements of <paramref name="source" />.</typeparam>
    /// <param name="source">An <see cref="IQueryable{T}" /> to return the first element of.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains the first element in <paramref name="source" />.
    /// </returns>
    /// <exception cref="ArgumentNullException"><paramref name="source" /> is <see langword="null" />.</exception>
    /// <exception cref="InvalidOperationException"><paramref name="source" /> contains no elements.</exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static Task<TSource> FirstAsync<TSource>(
        this IQueryable<TSource> source,
        CancellationToken cancellationToken = default)
        => ExecuteAsync<TSource, Task<TSource>>(QueryableMethods.FirstWithoutPredicate, source, cancellationToken);

    /// <summary>
    ///     Asynchronously returns the first element of a sequence that satisfies a specified condition.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <typeparam name="TSource">The type of the elements of <paramref name="source" />.</typeparam>
    /// <param name="source">An <see cref="IQueryable{T}" /> to return the first element of.</param>
    /// <param name="predicate">A function to test each element for a condition.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains the first element in <paramref name="source" /> that passes the test in
    ///     <paramref name="predicate" />.
    /// </returns>
    /// <exception cref="ArgumentNullException">
    ///     <paramref name="source" /> or <paramref name="predicate" /> is <see langword="null" />.
    /// </exception>
    /// <exception cref="InvalidOperationException">
    ///     <para>
    ///         No element satisfies the condition in <paramref name="predicate" />
    ///     </para>
    ///     <para>
    ///         -or -
    ///     </para>
    ///     <para>
    ///         <paramref name="source" /> contains no elements.
    ///     </para>
    /// </exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static Task<TSource> FirstAsync<TSource>(
        this IQueryable<TSource> source,
        Expression<Func<TSource, bool>> predicate,
        CancellationToken cancellationToken = default)
    {
        Check.NotNull(predicate);

        return ExecuteAsync<TSource, Task<TSource>>(QueryableMethods.FirstWithPredicate, source, predicate, cancellationToken);
    }

    /// <summary>
    ///     Asynchronously returns the first element of a sequence, or a default value if the sequence contains no elements.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <typeparam name="TSource">The type of the elements of <paramref name="source" />.</typeparam>
    /// <param name="source">An <see cref="IQueryable{T}" /> to return the first element of.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains <see langword="default" /> ( <typeparamref name="TSource" /> ) if
    ///     <paramref name="source" /> is empty; otherwise, the first element in <paramref name="source" />.
    /// </returns>
    /// <exception cref="ArgumentNullException"><paramref name="source" /> is <see langword="null" />.</exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static Task<TSource?> FirstOrDefaultAsync<TSource>(
        this IQueryable<TSource> source,
        CancellationToken cancellationToken = default)
        => ExecuteAsync<TSource, Task<TSource?>>(QueryableMethods.FirstOrDefaultWithoutPredicate, source, cancellationToken);

    /// <summary>
    ///     Asynchronously returns the first element of a sequence that satisfies a specified condition
    ///     or a default value if no such element is found.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <typeparam name="TSource">The type of the elements of <paramref name="source" />.</typeparam>
    /// <param name="source">An <see cref="IQueryable{T}" /> to return the first element of.</param>
    /// <param name="predicate">A function to test each element for a condition.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains <see langword="default" /> ( <typeparamref name="TSource" /> ) if <paramref name="source" />
    ///     is empty or if no element passes the test specified by <paramref name="predicate" />, otherwise, the first
    ///     element in <paramref name="source" /> that passes the test specified by <paramref name="predicate" />.
    /// </returns>
    /// <exception cref="ArgumentNullException">
    ///     <paramref name="source" /> or <paramref name="predicate" /> is <see langword="null" />.
    /// </exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static Task<TSource?> FirstOrDefaultAsync<TSource>(
        this IQueryable<TSource> source,
        Expression<Func<TSource, bool>> predicate,
        CancellationToken cancellationToken = default)
    {
        Check.NotNull(predicate);

        return ExecuteAsync<TSource, Task<TSource?>>(
            QueryableMethods.FirstOrDefaultWithPredicate, source, predicate, cancellationToken);
    }

    #endregion

    #region Last/LastOrDefault

    /// <summary>
    ///     Asynchronously returns the last element of a sequence.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <typeparam name="TSource">The type of the elements of <paramref name="source" />.</typeparam>
    /// <param name="source">An <see cref="IQueryable{T}" /> to return the last element of.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains the last element in <paramref name="source" />.
    /// </returns>
    /// <exception cref="ArgumentNullException"><paramref name="source" /> is <see langword="null" />.</exception>
    /// <exception cref="InvalidOperationException"><paramref name="source" /> contains no elements.</exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static Task<TSource> LastAsync<TSource>(
        this IQueryable<TSource> source,
        CancellationToken cancellationToken = default)
        => ExecuteAsync<TSource, Task<TSource>>(QueryableMethods.LastWithoutPredicate, source, cancellationToken);

    /// <summary>
    ///     Asynchronously returns the last element of a sequence that satisfies a specified condition.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <typeparam name="TSource">The type of the elements of <paramref name="source" />.</typeparam>
    /// <param name="source">An <see cref="IQueryable{T}" /> to return the last element of.</param>
    /// <param name="predicate">A function to test each element for a condition.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains the last element in <paramref name="source" /> that passes the test in
    ///     <paramref name="predicate" />.
    /// </returns>
    /// <exception cref="ArgumentNullException">
    ///     <paramref name="source" /> or <paramref name="predicate" /> is <see langword="null" />.
    /// </exception>
    /// <exception cref="InvalidOperationException">
    ///     <para>
    ///         No element satisfies the condition in <paramref name="predicate" />.
    ///     </para>
    ///     <para>
    ///         -or-
    ///     </para>
    ///     <para>
    ///         <paramref name="source" /> contains no elements.
    ///     </para>
    /// </exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static Task<TSource> LastAsync<TSource>(
        this IQueryable<TSource> source,
        Expression<Func<TSource, bool>> predicate,
        CancellationToken cancellationToken = default)
    {
        Check.NotNull(predicate);

        return ExecuteAsync<TSource, Task<TSource>>(QueryableMethods.LastWithPredicate, source, predicate, cancellationToken);
    }

    /// <summary>
    ///     Asynchronously returns the last element of a sequence, or a default value if the sequence contains no elements.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <typeparam name="TSource">The type of the elements of <paramref name="source" />.</typeparam>
    /// <param name="source">An <see cref="IQueryable{T}" /> to return the last element of.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains <see langword="default" /> ( <typeparamref name="TSource" /> ) if
    ///     <paramref name="source" /> is empty; otherwise, the last element in <paramref name="source" />.
    /// </returns>
    /// <exception cref="ArgumentNullException"><paramref name="source" /> is <see langword="null" />.</exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static Task<TSource?> LastOrDefaultAsync<TSource>(
        this IQueryable<TSource> source,
        CancellationToken cancellationToken = default)
        => ExecuteAsync<TSource, Task<TSource?>>(QueryableMethods.LastOrDefaultWithoutPredicate, source, cancellationToken);

    /// <summary>
    ///     Asynchronously returns the last element of a sequence that satisfies a specified condition
    ///     or a default value if no such element is found.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <typeparam name="TSource">The type of the elements of <paramref name="source" />.</typeparam>
    /// <param name="source">An <see cref="IQueryable{T}" /> to return the last element of.</param>
    /// <param name="predicate">A function to test each element for a condition.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains <see langword="default" /> ( <typeparamref name="TSource" /> ) if <paramref name="source" />
    ///     is empty or if no element passes the test specified by <paramref name="predicate" />, otherwise, the last
    ///     element in <paramref name="source" /> that passes the test specified by <paramref name="predicate" />.
    /// </returns>
    /// <exception cref="ArgumentNullException">
    ///     <paramref name="source" /> or <paramref name="predicate" /> is <see langword="null" />.
    /// </exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static Task<TSource?> LastOrDefaultAsync<TSource>(
        this IQueryable<TSource> source,
        Expression<Func<TSource, bool>> predicate,
        CancellationToken cancellationToken = default)
    {
        Check.NotNull(predicate);

        return ExecuteAsync<TSource, Task<TSource?>>(QueryableMethods.LastOrDefaultWithPredicate, source, predicate, cancellationToken);
    }

    #endregion

    #region Single/SingleOrDefault

    /// <summary>
    ///     Asynchronously returns the only element of a sequence, and throws an exception
    ///     if there is not exactly one element in the sequence.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <typeparam name="TSource">The type of the elements of <paramref name="source" />.</typeparam>
    /// <param name="source">An <see cref="IQueryable{T}" /> to return the single element of.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains the single element of the input sequence.
    /// </returns>
    /// <exception cref="ArgumentNullException"><paramref name="source" /> is <see langword="null" />.</exception>
    /// <exception cref="InvalidOperationException">
    ///     <para>
    ///         <paramref name="source" /> contains more than one elements.
    ///     </para>
    ///     <para>
    ///         -or-
    ///     </para>
    ///     <para>
    ///         <paramref name="source" /> contains no elements.
    ///     </para>
    /// </exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static Task<TSource> SingleAsync<TSource>(
        this IQueryable<TSource> source,
        CancellationToken cancellationToken = default)
        => ExecuteAsync<TSource, Task<TSource>>(QueryableMethods.SingleWithoutPredicate, source, cancellationToken);

    /// <summary>
    ///     Asynchronously returns the only element of a sequence that satisfies a specified condition,
    ///     and throws an exception if more than one such element exists.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <typeparam name="TSource">The type of the elements of <paramref name="source" />.</typeparam>
    /// <param name="source">An <see cref="IQueryable{T}" /> to return the single element of.</param>
    /// <param name="predicate">A function to test an element for a condition.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains the single element of the input sequence that satisfies the condition in
    ///     <paramref name="predicate" />.
    /// </returns>
    /// <exception cref="ArgumentNullException">
    ///     <paramref name="source" /> or <paramref name="predicate" /> is <see langword="null" />.
    /// </exception>
    /// <exception cref="InvalidOperationException">
    ///     <para>
    ///         No element satisfies the condition in <paramref name="predicate" />.
    ///     </para>
    ///     <para>
    ///         -or-
    ///     </para>
    ///     <para>
    ///         More than one element satisfies the condition in <paramref name="predicate" />.
    ///     </para>
    ///     <para>
    ///         -or-
    ///     </para>
    ///     <para>
    ///         <paramref name="source" /> contains no elements.
    ///     </para>
    /// </exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static Task<TSource> SingleAsync<TSource>(
        this IQueryable<TSource> source,
        Expression<Func<TSource, bool>> predicate,
        CancellationToken cancellationToken = default)
    {
        Check.NotNull(predicate);

        return ExecuteAsync<TSource, Task<TSource>>(QueryableMethods.SingleWithPredicate, source, predicate, cancellationToken);
    }

    /// <summary>
    ///     Asynchronously returns the only element of a sequence, or a default value if the sequence is empty;
    ///     this method throws an exception if there is more than one element in the sequence.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <typeparam name="TSource">The type of the elements of <paramref name="source" />.</typeparam>
    /// <param name="source">An <see cref="IQueryable{T}" /> to return the single element of.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains the single element of the input sequence, or <see langword="default" /> (
    ///     <typeparamref name="TSource" />)
    ///     if the sequence contains no elements.
    /// </returns>
    /// <exception cref="ArgumentNullException"><paramref name="source" /> is <see langword="null" />.</exception>
    /// <exception cref="InvalidOperationException"><paramref name="source" /> contains more than one element.</exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static Task<TSource?> SingleOrDefaultAsync<TSource>(
        this IQueryable<TSource> source,
        CancellationToken cancellationToken = default)
        => ExecuteAsync<TSource, Task<TSource?>>(QueryableMethods.SingleOrDefaultWithoutPredicate, source, cancellationToken);

    /// <summary>
    ///     Asynchronously returns the only element of a sequence that satisfies a specified condition or
    ///     a default value if no such element exists; this method throws an exception if more than one element
    ///     satisfies the condition.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <typeparam name="TSource">The type of the elements of <paramref name="source" />.</typeparam>
    /// <param name="source">An <see cref="IQueryable{T}" /> to return the single element of.</param>
    /// <param name="predicate">A function to test an element for a condition.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains the single element of the input sequence that satisfies the condition in
    ///     <paramref name="predicate" />, or <see langword="default" /> ( <typeparamref name="TSource" /> ) if no such element is found.
    /// </returns>
    /// <exception cref="ArgumentNullException">
    ///     <paramref name="source" /> or <paramref name="predicate" /> is <see langword="null" />.
    /// </exception>
    /// <exception cref="InvalidOperationException">
    ///     More than one element satisfies the condition in <paramref name="predicate" />.
    /// </exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static Task<TSource?> SingleOrDefaultAsync<TSource>(
        this IQueryable<TSource> source,
        Expression<Func<TSource, bool>> predicate,
        CancellationToken cancellationToken = default)
    {
        Check.NotNull(predicate);

        return ExecuteAsync<TSource, Task<TSource?>>(
            QueryableMethods.SingleOrDefaultWithPredicate, source, predicate, cancellationToken);
    }

    #endregion

    #region Min

    /// <summary>
    ///     Asynchronously returns the minimum value of a sequence.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <typeparam name="TSource">The type of the elements of <paramref name="source" />.</typeparam>
    /// <param name="source">An <see cref="IQueryable{T}" /> that contains the elements to determine the minimum of.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains the minimum value in the sequence.
    /// </returns>
    /// <exception cref="ArgumentNullException"><paramref name="source" /> is <see langword="null" />.</exception>
    /// <exception cref="InvalidOperationException"><paramref name="source" /> contains no elements.</exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static Task<TSource> MinAsync<TSource>(
        this IQueryable<TSource> source,
        CancellationToken cancellationToken = default)
        => ExecuteAsync<TSource, Task<TSource>>(QueryableMethods.MinWithoutSelector, source, cancellationToken);

    /// <summary>
    ///     Asynchronously invokes a projection function on each element of a sequence and returns the minimum resulting value.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <typeparam name="TSource">The type of the elements of <paramref name="source" />.</typeparam>
    /// <typeparam name="TResult">
    ///     The type of the value returned by the function represented by <paramref name="selector" />.
    /// </typeparam>
    /// <param name="source">An <see cref="IQueryable{T}" /> that contains the elements to determine the minimum of.</param>
    /// <param name="selector">A projection function to apply to each element.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains the minimum value in the sequence.
    /// </returns>
    /// <exception cref="ArgumentNullException">
    ///     <paramref name="source" /> or <paramref name="selector" /> is <see langword="null" />.
    /// </exception>
    /// <exception cref="InvalidOperationException"><paramref name="source" /> contains no elements.</exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static Task<TResult> MinAsync<TSource, TResult>(
        this IQueryable<TSource> source,
        Expression<Func<TSource, TResult>> selector,
        CancellationToken cancellationToken = default)
    {
        Check.NotNull(selector);

        return ExecuteAsync<TSource, Task<TResult>>(QueryableMethods.MinWithSelector, source, selector, cancellationToken);
    }

    #endregion

    #region Max

    /// <summary>
    ///     Asynchronously returns the maximum value of a sequence.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <typeparam name="TSource">The type of the elements of <paramref name="source" />.</typeparam>
    /// <param name="source">An <see cref="IQueryable{T}" /> that contains the elements to determine the maximum of.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains the maximum value in the sequence.
    /// </returns>
    /// <exception cref="ArgumentNullException"><paramref name="source" /> is <see langword="null" />.</exception>
    /// <exception cref="InvalidOperationException"><paramref name="source" /> contains no elements.</exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static Task<TSource> MaxAsync<TSource>(
        this IQueryable<TSource> source,
        CancellationToken cancellationToken = default)
        => ExecuteAsync<TSource, Task<TSource>>(QueryableMethods.MaxWithoutSelector, source, cancellationToken);

    /// <summary>
    ///     Asynchronously invokes a projection function on each element of a sequence and returns the maximum resulting value.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <typeparam name="TSource">The type of the elements of <paramref name="source" />.</typeparam>
    /// <typeparam name="TResult">
    ///     The type of the value returned by the function represented by <paramref name="selector" />.
    /// </typeparam>
    /// <param name="source">An <see cref="IQueryable{T}" /> that contains the elements to determine the maximum of.</param>
    /// <param name="selector">A projection function to apply to each element.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains the maximum value in the sequence.
    /// </returns>
    /// <exception cref="ArgumentNullException">
    ///     <paramref name="source" /> or <paramref name="selector" /> is <see langword="null" />.
    /// </exception>
    /// <exception cref="InvalidOperationException"><paramref name="source" /> contains no elements.</exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static Task<TResult> MaxAsync<TSource, TResult>(
        this IQueryable<TSource> source,
        Expression<Func<TSource, TResult>> selector,
        CancellationToken cancellationToken = default)
    {
        Check.NotNull(selector);

        return ExecuteAsync<TSource, Task<TResult>>(QueryableMethods.MaxWithSelector, source, selector, cancellationToken);
    }

    #endregion

    #region Sum

    /// <summary>
    ///     Asynchronously computes the sum of a sequence of values.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <param name="source">A sequence of values to calculate the sum of.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains the sum of the values in the sequence.
    /// </returns>
    /// <exception cref="ArgumentNullException"><paramref name="source" /> is <see langword="null" />.</exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static Task<decimal> SumAsync(
        this IQueryable<decimal> source,
        CancellationToken cancellationToken = default)
        => ExecuteAsync<decimal, Task<decimal>>(QueryableMethods.GetSumWithoutSelector(typeof(decimal)), source, cancellationToken);

    /// <summary>
    ///     Asynchronously computes the sum of a sequence of values.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <param name="source">A sequence of values to calculate the sum of.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains the sum of the values in the sequence.
    /// </returns>
    /// <exception cref="ArgumentNullException"><paramref name="source" /> is <see langword="null" />.</exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static Task<decimal?> SumAsync(
        this IQueryable<decimal?> source,
        CancellationToken cancellationToken = default)
        => ExecuteAsync<decimal?, Task<decimal?>>(
            QueryableMethods.GetSumWithoutSelector(typeof(decimal?)), source, cancellationToken);

    /// <summary>
    ///     Asynchronously computes the sum of the sequence of values that is obtained by invoking a projection function on
    ///     each element of the input sequence.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <param name="source">A sequence of values of type <typeparamref name="TSource" />.</param>
    /// <param name="selector">A projection function to apply to each element.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains the sum of the projected values.
    /// </returns>
    /// <exception cref="ArgumentNullException">
    ///     <paramref name="source" /> or <paramref name="selector" /> is <see langword="null" />.
    /// </exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static Task<decimal> SumAsync<TSource>(
        this IQueryable<TSource> source,
        Expression<Func<TSource, decimal>> selector,
        CancellationToken cancellationToken = default)
    {
        Check.NotNull(selector);

        return ExecuteAsync<TSource, Task<decimal>>(
            QueryableMethods.GetSumWithSelector(typeof(decimal)), source, selector, cancellationToken);
    }

    /// <summary>
    ///     Asynchronously computes the sum of the sequence of values that is obtained by invoking a projection function on
    ///     each element of the input sequence.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <param name="source">A sequence of values of type <typeparamref name="TSource" />.</param>
    /// <param name="selector">A projection function to apply to each element.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains the sum of the projected values.
    /// </returns>
    /// <exception cref="ArgumentNullException">
    ///     <paramref name="source" /> or <paramref name="selector" /> is <see langword="null" />.
    /// </exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static Task<decimal?> SumAsync<TSource>(
        this IQueryable<TSource> source,
        Expression<Func<TSource, decimal?>> selector,
        CancellationToken cancellationToken = default)
    {
        Check.NotNull(selector);

        return ExecuteAsync<TSource, Task<decimal?>>(
            QueryableMethods.GetSumWithSelector(typeof(decimal?)), source, selector, cancellationToken);
    }

    /// <summary>
    ///     Asynchronously computes the sum of a sequence of values.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <param name="source">A sequence of values to calculate the sum of.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains the sum of the values in the sequence.
    /// </returns>
    /// <exception cref="ArgumentNullException"><paramref name="source" /> is <see langword="null" />.</exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static Task<int> SumAsync(
        this IQueryable<int> source,
        CancellationToken cancellationToken = default)
        => ExecuteAsync<int, Task<int>>(QueryableMethods.GetSumWithoutSelector(typeof(int)), source, cancellationToken);

    /// <summary>
    ///     Asynchronously computes the sum of a sequence of values.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <param name="source">A sequence of values to calculate the sum of.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains the sum of the values in the sequence.
    /// </returns>
    /// <exception cref="ArgumentNullException"><paramref name="source" /> is <see langword="null" />.</exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static Task<int?> SumAsync(
        this IQueryable<int?> source,
        CancellationToken cancellationToken = default)
        => ExecuteAsync<int?, Task<int?>>(QueryableMethods.GetSumWithoutSelector(typeof(int?)), source, cancellationToken);

    /// <summary>
    ///     Asynchronously computes the sum of the sequence of values that is obtained by invoking a projection function on
    ///     each element of the input sequence.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <param name="source">A sequence of values of type <typeparamref name="TSource" />.</param>
    /// <param name="selector">A projection function to apply to each element.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains the sum of the projected values.
    /// </returns>
    /// <exception cref="ArgumentNullException">
    ///     <paramref name="source" /> or <paramref name="selector" /> is <see langword="null" />.
    /// </exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static Task<int> SumAsync<TSource>(
        this IQueryable<TSource> source,
        Expression<Func<TSource, int>> selector,
        CancellationToken cancellationToken = default)
    {
        Check.NotNull(selector);

        return ExecuteAsync<TSource, Task<int>>(QueryableMethods.GetSumWithSelector(typeof(int)), source, selector, cancellationToken);
    }

    /// <summary>
    ///     Asynchronously computes the sum of the sequence of values that is obtained by invoking a projection function on
    ///     each element of the input sequence.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <param name="source">A sequence of values of type <typeparamref name="TSource" />.</param>
    /// <param name="selector">A projection function to apply to each element.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains the sum of the projected values.
    /// </returns>
    /// <exception cref="ArgumentNullException">
    ///     <paramref name="source" /> or <paramref name="selector" /> is <see langword="null" />.
    /// </exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static Task<int?> SumAsync<TSource>(
        this IQueryable<TSource> source,
        Expression<Func<TSource, int?>> selector,
        CancellationToken cancellationToken = default)
    {
        Check.NotNull(selector);

        return ExecuteAsync<TSource, Task<int?>>(
            QueryableMethods.GetSumWithSelector(typeof(int?)), source, selector, cancellationToken);
    }

    /// <summary>
    ///     Asynchronously computes the sum of a sequence of values.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <param name="source">A sequence of values to calculate the sum of.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains the sum of the values in the sequence.
    /// </returns>
    /// <exception cref="ArgumentNullException"><paramref name="source" /> is <see langword="null" />.</exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static Task<long> SumAsync(
        this IQueryable<long> source,
        CancellationToken cancellationToken = default)
        => ExecuteAsync<long, Task<long>>(QueryableMethods.GetSumWithoutSelector(typeof(long)), source, cancellationToken);

    /// <summary>
    ///     Asynchronously computes the sum of a sequence of values.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <param name="source">A sequence of values to calculate the sum of.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains the sum of the values in the sequence.
    /// </returns>
    /// <exception cref="ArgumentNullException"><paramref name="source" /> is <see langword="null" />.</exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static Task<long?> SumAsync(
        this IQueryable<long?> source,
        CancellationToken cancellationToken = default)
        => ExecuteAsync<long?, Task<long?>>(QueryableMethods.GetSumWithoutSelector(typeof(long?)), source, cancellationToken);

    /// <summary>
    ///     Asynchronously computes the sum of the sequence of values that is obtained by invoking a projection function on
    ///     each element of the input sequence.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <param name="source">A sequence of values of type <typeparamref name="TSource" />.</param>
    /// <param name="selector">A projection function to apply to each element.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains the sum of the projected values.
    /// </returns>
    /// <exception cref="ArgumentNullException">
    ///     <paramref name="source" /> or <paramref name="selector" /> is <see langword="null" />.
    /// </exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static Task<long> SumAsync<TSource>(
        this IQueryable<TSource> source,
        Expression<Func<TSource, long>> selector,
        CancellationToken cancellationToken = default)
    {
        Check.NotNull(selector);

        return ExecuteAsync<TSource, Task<long>>(
            QueryableMethods.GetSumWithSelector(typeof(long)), source, selector, cancellationToken);
    }

    /// <summary>
    ///     Asynchronously computes the sum of the sequence of values that is obtained by invoking a projection function on
    ///     each element of the input sequence.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <param name="source">A sequence of values of type <typeparamref name="TSource" />.</param>
    /// <param name="selector">A projection function to apply to each element.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains the sum of the projected values.
    /// </returns>
    /// <exception cref="ArgumentNullException">
    ///     <paramref name="source" /> or <paramref name="selector" /> is <see langword="null" />.
    /// </exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static Task<long?> SumAsync<TSource>(
        this IQueryable<TSource> source,
        Expression<Func<TSource, long?>> selector,
        CancellationToken cancellationToken = default)
    {
        Check.NotNull(selector);

        return ExecuteAsync<TSource, Task<long?>>(
            QueryableMethods.GetSumWithSelector(typeof(long?)), source, selector, cancellationToken);
    }

    /// <summary>
    ///     Asynchronously computes the sum of a sequence of values.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <param name="source">A sequence of values to calculate the sum of.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains the sum of the values in the sequence.
    /// </returns>
    /// <exception cref="ArgumentNullException"><paramref name="source" /> is <see langword="null" />.</exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static Task<double> SumAsync(
        this IQueryable<double> source,
        CancellationToken cancellationToken = default)
        => ExecuteAsync<double, Task<double>>(QueryableMethods.GetSumWithoutSelector(typeof(double)), source, cancellationToken);

    /// <summary>
    ///     Asynchronously computes the sum of a sequence of values.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <param name="source">A sequence of values to calculate the sum of.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains the sum of the values in the sequence.
    /// </returns>
    /// <exception cref="ArgumentNullException"><paramref name="source" /> is <see langword="null" />.</exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static Task<double?> SumAsync(
        this IQueryable<double?> source,
        CancellationToken cancellationToken = default)
        => ExecuteAsync<double?, Task<double?>>(QueryableMethods.GetSumWithoutSelector(typeof(double?)), source, cancellationToken);

    /// <summary>
    ///     Asynchronously computes the sum of the sequence of values that is obtained by invoking a projection function on
    ///     each element of the input sequence.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <param name="source">A sequence of values of type <typeparamref name="TSource" />.</param>
    /// <param name="selector">A projection function to apply to each element.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains the sum of the projected values.
    /// </returns>
    /// <exception cref="ArgumentNullException">
    ///     <paramref name="source" /> or <paramref name="selector" /> is <see langword="null" />.
    /// </exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static Task<double> SumAsync<TSource>(
        this IQueryable<TSource> source,
        Expression<Func<TSource, double>> selector,
        CancellationToken cancellationToken = default)
    {
        Check.NotNull(selector);

        return ExecuteAsync<TSource, Task<double>>(
            QueryableMethods.GetSumWithSelector(typeof(double)), source, selector, cancellationToken);
    }

    /// <summary>
    ///     Asynchronously computes the sum of the sequence of values that is obtained by invoking a projection function on
    ///     each element of the input sequence.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <param name="source">A sequence of values of type <typeparamref name="TSource" />.</param>
    /// <param name="selector">A projection function to apply to each element.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains the sum of the projected values.
    /// </returns>
    /// <exception cref="ArgumentNullException">
    ///     <paramref name="source" /> or <paramref name="selector" /> is <see langword="null" />.
    /// </exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static Task<double?> SumAsync<TSource>(
        this IQueryable<TSource> source,
        Expression<Func<TSource, double?>> selector,
        CancellationToken cancellationToken = default)
    {
        Check.NotNull(selector);

        return ExecuteAsync<TSource, Task<double?>>(
            QueryableMethods.GetSumWithSelector(typeof(double?)), source, selector, cancellationToken);
    }

    /// <summary>
    ///     Asynchronously computes the sum of a sequence of values.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <param name="source">A sequence of values to calculate the sum of.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains the sum of the values in the sequence.
    /// </returns>
    /// <exception cref="ArgumentNullException"><paramref name="source" /> is <see langword="null" />.</exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static Task<float> SumAsync(
        this IQueryable<float> source,
        CancellationToken cancellationToken = default)
        => ExecuteAsync<float, Task<float>>(QueryableMethods.GetSumWithoutSelector(typeof(float)), source, cancellationToken);

    /// <summary>
    ///     Asynchronously computes the sum of a sequence of values.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <param name="source">A sequence of values to calculate the sum of.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains the sum of the values in the sequence.
    /// </returns>
    /// <exception cref="ArgumentNullException"><paramref name="source" /> is <see langword="null" />.</exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static Task<float?> SumAsync(
        this IQueryable<float?> source,
        CancellationToken cancellationToken = default)
        => ExecuteAsync<float?, Task<float?>>(QueryableMethods.GetSumWithoutSelector(typeof(float?)), source, cancellationToken);

    /// <summary>
    ///     Asynchronously computes the sum of the sequence of values that is obtained by invoking a projection function on
    ///     each element of the input sequence.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <param name="source">A sequence of values of type <typeparamref name="TSource" />.</param>
    /// <param name="selector">A projection function to apply to each element.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains the sum of the projected values.
    /// </returns>
    /// <exception cref="ArgumentNullException">
    ///     <paramref name="source" /> or <paramref name="selector" /> is <see langword="null" />.
    /// </exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static Task<float> SumAsync<TSource>(
        this IQueryable<TSource> source,
        Expression<Func<TSource, float>> selector,
        CancellationToken cancellationToken = default)
    {
        Check.NotNull(selector);

        return ExecuteAsync<TSource, Task<float>>(
            QueryableMethods.GetSumWithSelector(typeof(float)), source, selector, cancellationToken);
    }

    /// <summary>
    ///     Asynchronously computes the sum of the sequence of values that is obtained by invoking a projection function on
    ///     each element of the input sequence.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <param name="source">A sequence of values of type <typeparamref name="TSource" />.</param>
    /// <param name="selector">A projection function to apply to each element.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains the sum of the projected values.
    /// </returns>
    /// <exception cref="ArgumentNullException">
    ///     <paramref name="source" /> or <paramref name="selector" /> is <see langword="null" />.
    /// </exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static Task<float?> SumAsync<TSource>(
        this IQueryable<TSource> source,
        Expression<Func<TSource, float?>> selector,
        CancellationToken cancellationToken = default)
    {
        Check.NotNull(selector);

        return ExecuteAsync<TSource, Task<float?>>(
            QueryableMethods.GetSumWithSelector(typeof(float?)), source, selector, cancellationToken);
    }

    #endregion

    #region Average

    /// <summary>
    ///     Asynchronously computes the average of a sequence of values.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <param name="source">A sequence of values to calculate the average of.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains the average of the sequence of values.
    /// </returns>
    /// <exception cref="ArgumentNullException"><paramref name="source" /> is <see langword="null" />.</exception>
    /// <exception cref="InvalidOperationException"><paramref name="source" /> contains no elements.</exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static Task<decimal> AverageAsync(
        this IQueryable<decimal> source,
        CancellationToken cancellationToken = default)
        => ExecuteAsync<decimal, Task<decimal>>(
            QueryableMethods.GetAverageWithoutSelector(typeof(decimal)), source, cancellationToken);

    /// <summary>
    ///     Asynchronously computes the average of a sequence of values.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <param name="source">A sequence of values to calculate the average of.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains the average of the sequence of values.
    /// </returns>
    /// <exception cref="ArgumentNullException"><paramref name="source" /> is <see langword="null" />.</exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static Task<decimal?> AverageAsync(
        this IQueryable<decimal?> source,
        CancellationToken cancellationToken = default)
        => ExecuteAsync<decimal?, Task<decimal?>>(
            QueryableMethods.GetAverageWithoutSelector(typeof(decimal?)), source, cancellationToken);

    /// <summary>
    ///     Asynchronously computes the average of a sequence of values that is obtained
    ///     by invoking a projection function on each element of the input sequence.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <typeparam name="TSource">The type of the elements of <paramref name="source" />.</typeparam>
    /// <param name="source">A sequence of values of type <typeparamref name="TSource" />.</param>
    /// <param name="selector">A projection function to apply to each element.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains the average of the projected values.
    /// </returns>
    /// <exception cref="ArgumentNullException">
    ///     <paramref name="source" /> or <paramref name="selector" /> is <see langword="null" />.
    /// </exception>
    /// <exception cref="InvalidOperationException"><paramref name="source" /> contains no elements.</exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static Task<decimal> AverageAsync<TSource>(
        this IQueryable<TSource> source,
        Expression<Func<TSource, decimal>> selector,
        CancellationToken cancellationToken = default)
    {
        Check.NotNull(selector);

        return ExecuteAsync<TSource, Task<decimal>>(
            QueryableMethods.GetAverageWithSelector(typeof(decimal)), source, selector, cancellationToken);
    }

    /// <summary>
    ///     Asynchronously computes the average of a sequence of values that is obtained
    ///     by invoking a projection function on each element of the input sequence.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <typeparam name="TSource">The type of the elements of <paramref name="source" />.</typeparam>
    /// <param name="source">A sequence of values of type <typeparamref name="TSource" />.</param>
    /// <param name="selector">A projection function to apply to each element.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains the average of the projected values.
    /// </returns>
    /// <exception cref="ArgumentNullException">
    ///     <paramref name="source" /> or <paramref name="selector" /> is <see langword="null" />.
    /// </exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static Task<decimal?> AverageAsync<TSource>(
        this IQueryable<TSource> source,
        Expression<Func<TSource, decimal?>> selector,
        CancellationToken cancellationToken = default)
    {
        Check.NotNull(selector);

        return ExecuteAsync<TSource, Task<decimal?>>(
            QueryableMethods.GetAverageWithSelector(typeof(decimal?)), source, selector, cancellationToken);
    }

    /// <summary>
    ///     Asynchronously computes the average of a sequence of values.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <param name="source">A sequence of values to calculate the average of.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains the average of the sequence of values.
    /// </returns>
    /// <exception cref="ArgumentNullException"><paramref name="source" /> is <see langword="null" />.</exception>
    /// <exception cref="InvalidOperationException"><paramref name="source" /> contains no elements.</exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static Task<double> AverageAsync(
        this IQueryable<int> source,
        CancellationToken cancellationToken = default)
        => ExecuteAsync<int, Task<double>>(QueryableMethods.GetAverageWithoutSelector(typeof(int)), source, cancellationToken);

    /// <summary>
    ///     Asynchronously computes the average of a sequence of values.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <param name="source">A sequence of values to calculate the average of.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains the average of the sequence of values.
    /// </returns>
    /// <exception cref="ArgumentNullException"><paramref name="source" /> is <see langword="null" />.</exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static Task<double?> AverageAsync(
        this IQueryable<int?> source,
        CancellationToken cancellationToken = default)
        => ExecuteAsync<int?, Task<double?>>(QueryableMethods.GetAverageWithoutSelector(typeof(int?)), source, cancellationToken);

    /// <summary>
    ///     Asynchronously computes the average of a sequence of values that is obtained
    ///     by invoking a projection function on each element of the input sequence.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <typeparam name="TSource">The type of the elements of <paramref name="source" />.</typeparam>
    /// <param name="source">A sequence of values of type <typeparamref name="TSource" />.</param>
    /// <param name="selector">A projection function to apply to each element.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains the average of the projected values.
    /// </returns>
    /// <exception cref="ArgumentNullException">
    ///     <paramref name="source" /> or <paramref name="selector" /> is <see langword="null" />.
    /// </exception>
    /// <exception cref="InvalidOperationException"><paramref name="source" /> contains no elements.</exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static Task<double> AverageAsync<TSource>(
        this IQueryable<TSource> source,
        Expression<Func<TSource, int>> selector,
        CancellationToken cancellationToken = default)
    {
        Check.NotNull(selector);

        return ExecuteAsync<TSource, Task<double>>(
            QueryableMethods.GetAverageWithSelector(typeof(int)), source, selector, cancellationToken);
    }

    /// <summary>
    ///     Asynchronously computes the average of a sequence of values that is obtained
    ///     by invoking a projection function on each element of the input sequence.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <typeparam name="TSource">The type of the elements of <paramref name="source" />.</typeparam>
    /// <param name="source">A sequence of values of type <typeparamref name="TSource" />.</param>
    /// <param name="selector">A projection function to apply to each element.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains the average of the projected values.
    /// </returns>
    /// <exception cref="ArgumentNullException">
    ///     <paramref name="source" /> or <paramref name="selector" /> is <see langword="null" />.
    /// </exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static Task<double?> AverageAsync<TSource>(
        this IQueryable<TSource> source,
        Expression<Func<TSource, int?>> selector,
        CancellationToken cancellationToken = default)
    {
        Check.NotNull(selector);

        return ExecuteAsync<TSource, Task<double?>>(
            QueryableMethods.GetAverageWithSelector(typeof(int?)), source, selector, cancellationToken);
    }

    /// <summary>
    ///     Asynchronously computes the average of a sequence of values.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <param name="source">A sequence of values to calculate the average of.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains the average of the sequence of values.
    /// </returns>
    /// <exception cref="ArgumentNullException"><paramref name="source" /> is <see langword="null" />.</exception>
    /// <exception cref="InvalidOperationException"><paramref name="source" /> contains no elements.</exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static Task<double> AverageAsync(
        this IQueryable<long> source,
        CancellationToken cancellationToken = default)
        => ExecuteAsync<long, Task<double>>(QueryableMethods.GetAverageWithoutSelector(typeof(long)), source, cancellationToken);

    /// <summary>
    ///     Asynchronously computes the average of a sequence of values.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <param name="source">A sequence of values to calculate the average of.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains the average of the sequence of values.
    /// </returns>
    /// <exception cref="ArgumentNullException"><paramref name="source" /> is <see langword="null" />.</exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static Task<double?> AverageAsync(
        this IQueryable<long?> source,
        CancellationToken cancellationToken = default)
        => ExecuteAsync<long?, Task<double?>>(QueryableMethods.GetAverageWithoutSelector(typeof(long?)), source, cancellationToken);

    /// <summary>
    ///     Asynchronously computes the average of a sequence of values that is obtained
    ///     by invoking a projection function on each element of the input sequence.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <typeparam name="TSource">The type of the elements of <paramref name="source" />.</typeparam>
    /// <param name="source">A sequence of values of type <typeparamref name="TSource" />.</param>
    /// <param name="selector">A projection function to apply to each element.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains the average of the projected values.
    /// </returns>
    /// <exception cref="ArgumentNullException">
    ///     <paramref name="source" /> or <paramref name="selector" /> is <see langword="null" />.
    /// </exception>
    /// <exception cref="InvalidOperationException"><paramref name="source" /> contains no elements.</exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static Task<double> AverageAsync<TSource>(
        this IQueryable<TSource> source,
        Expression<Func<TSource, long>> selector,
        CancellationToken cancellationToken = default)
    {
        Check.NotNull(selector);

        return ExecuteAsync<TSource, Task<double>>(
            QueryableMethods.GetAverageWithSelector(typeof(long)), source, selector, cancellationToken);
    }

    /// <summary>
    ///     Asynchronously computes the average of a sequence of values that is obtained
    ///     by invoking a projection function on each element of the input sequence.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <typeparam name="TSource">The type of the elements of <paramref name="source" />.</typeparam>
    /// <param name="source">A sequence of values of type <typeparamref name="TSource" />.</param>
    /// <param name="selector">A projection function to apply to each element.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains the average of the projected values.
    /// </returns>
    /// <exception cref="ArgumentNullException">
    ///     <paramref name="source" /> or <paramref name="selector" /> is <see langword="null" />.
    /// </exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static Task<double?> AverageAsync<TSource>(
        this IQueryable<TSource> source,
        Expression<Func<TSource, long?>> selector,
        CancellationToken cancellationToken = default)
    {
        Check.NotNull(selector);

        return ExecuteAsync<TSource, Task<double?>>(
            QueryableMethods.GetAverageWithSelector(typeof(long?)), source, selector, cancellationToken);
    }

    /// <summary>
    ///     Asynchronously computes the average of a sequence of values.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <param name="source">A sequence of values to calculate the average of.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains the average of the sequence of values.
    /// </returns>
    /// <exception cref="ArgumentNullException"><paramref name="source" /> is <see langword="null" />.</exception>
    /// <exception cref="InvalidOperationException"><paramref name="source" /> contains no elements.</exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static Task<double> AverageAsync(
        this IQueryable<double> source,
        CancellationToken cancellationToken = default)
        => ExecuteAsync<double, Task<double>>(
            QueryableMethods.GetAverageWithoutSelector(typeof(double)), source, cancellationToken);

    /// <summary>
    ///     Asynchronously computes the average of a sequence of values.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <param name="source">A sequence of values to calculate the average of.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains the average of the sequence of values.
    /// </returns>
    /// <exception cref="ArgumentNullException"><paramref name="source" /> is <see langword="null" />.</exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static Task<double?> AverageAsync(
        this IQueryable<double?> source,
        CancellationToken cancellationToken = default)
        => ExecuteAsync<double?, Task<double?>>(
            QueryableMethods.GetAverageWithoutSelector(typeof(double?)), source, cancellationToken);

    /// <summary>
    ///     Asynchronously computes the average of a sequence of values that is obtained
    ///     by invoking a projection function on each element of the input sequence.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <typeparam name="TSource">The type of the elements of <paramref name="source" />.</typeparam>
    /// <param name="source">A sequence of values of type <typeparamref name="TSource" />.</param>
    /// <param name="selector">A projection function to apply to each element.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains the average of the projected values.
    /// </returns>
    /// <exception cref="ArgumentNullException">
    ///     <paramref name="source" /> or <paramref name="selector" /> is <see langword="null" />.
    /// </exception>
    /// <exception cref="InvalidOperationException"><paramref name="source" /> contains no elements.</exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static Task<double> AverageAsync<TSource>(
        this IQueryable<TSource> source,
        Expression<Func<TSource, double>> selector,
        CancellationToken cancellationToken = default)
    {
        Check.NotNull(selector);

        return ExecuteAsync<TSource, Task<double>>(
            QueryableMethods.GetAverageWithSelector(typeof(double)), source, selector, cancellationToken);
    }

    /// <summary>
    ///     Asynchronously computes the average of a sequence of values that is obtained
    ///     by invoking a projection function on each element of the input sequence.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <typeparam name="TSource">The type of the elements of <paramref name="source" />.</typeparam>
    /// <param name="source">A sequence of values of type <typeparamref name="TSource" />.</param>
    /// <param name="selector">A projection function to apply to each element.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains the average of the projected values.
    /// </returns>
    /// <exception cref="ArgumentNullException">
    ///     <paramref name="source" /> or <paramref name="selector" /> is <see langword="null" />.
    /// </exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static Task<double?> AverageAsync<TSource>(
        this IQueryable<TSource> source,
        Expression<Func<TSource, double?>> selector,
        CancellationToken cancellationToken = default)
    {
        Check.NotNull(selector);

        return ExecuteAsync<TSource, Task<double?>>(
            QueryableMethods.GetAverageWithSelector(typeof(double?)), source, selector, cancellationToken);
    }

    /// <summary>
    ///     Asynchronously computes the average of a sequence of values.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <param name="source">A sequence of values to calculate the average of.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains the average of the sequence of values.
    /// </returns>
    /// <exception cref="ArgumentNullException"><paramref name="source" /> is <see langword="null" />.</exception>
    /// <exception cref="InvalidOperationException"><paramref name="source" /> contains no elements.</exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static Task<float> AverageAsync(
        this IQueryable<float> source,
        CancellationToken cancellationToken = default)
        => ExecuteAsync<float, Task<float>>(QueryableMethods.GetAverageWithoutSelector(typeof(float)), source, cancellationToken);

    /// <summary>
    ///     Asynchronously computes the average of a sequence of values.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <param name="source">A sequence of values to calculate the average of.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains the average of the sequence of values.
    /// </returns>
    /// <exception cref="ArgumentNullException"><paramref name="source" /> is <see langword="null" />.</exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static Task<float?> AverageAsync(
        this IQueryable<float?> source,
        CancellationToken cancellationToken = default)
        => ExecuteAsync<float?, Task<float?>>(
            QueryableMethods.GetAverageWithoutSelector(typeof(float?)), source, cancellationToken);

    /// <summary>
    ///     Asynchronously computes the average of a sequence of values that is obtained
    ///     by invoking a projection function on each element of the input sequence.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <typeparam name="TSource">The type of the elements of <paramref name="source" />.</typeparam>
    /// <param name="source">A sequence of values of type <typeparamref name="TSource" />.</param>
    /// <param name="selector">A projection function to apply to each element.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains the average of the projected values.
    /// </returns>
    /// <exception cref="ArgumentNullException">
    ///     <paramref name="source" /> or <paramref name="selector" /> is <see langword="null" />.
    /// </exception>
    /// <exception cref="InvalidOperationException"><paramref name="source" /> contains no elements.</exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static Task<float> AverageAsync<TSource>(
        this IQueryable<TSource> source,
        Expression<Func<TSource, float>> selector,
        CancellationToken cancellationToken = default)
    {
        Check.NotNull(selector);

        return ExecuteAsync<TSource, Task<float>>(
            QueryableMethods.GetAverageWithSelector(typeof(float)), source, selector, cancellationToken);
    }

    /// <summary>
    ///     Asynchronously computes the average of a sequence of values that is obtained
    ///     by invoking a projection function on each element of the input sequence.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <typeparam name="TSource">The type of the elements of <paramref name="source" />.</typeparam>
    /// <param name="source">A sequence of values of type <typeparamref name="TSource" />.</param>
    /// <param name="selector">A projection function to apply to each element.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains the average of the projected values.
    /// </returns>
    /// <exception cref="ArgumentNullException">
    ///     <paramref name="source" /> or <paramref name="selector" /> is <see langword="null" />.
    /// </exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static Task<float?> AverageAsync<TSource>(
        this IQueryable<TSource> source,
        Expression<Func<TSource, float?>> selector,
        CancellationToken cancellationToken = default)
    {
        Check.NotNull(selector);

        return ExecuteAsync<TSource, Task<float?>>(
            QueryableMethods.GetAverageWithSelector(typeof(float?)), source, selector, cancellationToken);
    }

    #endregion

    #region Contains

    /// <summary>
    ///     Asynchronously determines whether a sequence contains a specified element by using the default equality comparer.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <typeparam name="TSource">The type of the elements of <paramref name="source" />.</typeparam>
    /// <param name="source">An <see cref="IQueryable{T}" /> to return the single element of.</param>
    /// <param name="item">The object to locate in the sequence.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains <see langword="true" /> if the input sequence contains the specified value; otherwise,
    ///     <see langword="false" />.
    /// </returns>
    /// <exception cref="ArgumentNullException"><paramref name="source" /> is <see langword="null" />.</exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static Task<bool> ContainsAsync<TSource>(
        this IQueryable<TSource> source,
        TSource item,
        CancellationToken cancellationToken = default)
        => ExecuteAsync<TSource, Task<bool>>(
            QueryableMethods.Contains,
            source,
            Expression.Constant(item, typeof(TSource)),
            cancellationToken);

    #endregion

    #region ToList/Array

    /// <summary>
    ///     Asynchronously creates a <see cref="List{T}" /> from an <see cref="IQueryable{T}" /> by enumerating it
    ///     asynchronously.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <typeparam name="TSource">The type of the elements of <paramref name="source" />.</typeparam>
    /// <param name="source">An <see cref="IQueryable{T}" /> to create a list from.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains a <see cref="List{T}" /> that contains elements from the input sequence.
    /// </returns>
    /// <exception cref="ArgumentNullException"><paramref name="source" /> is <see langword="null" />.</exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static async Task<List<TSource>> ToListAsync<TSource>(
        this IQueryable<TSource> source,
        CancellationToken cancellationToken = default)
    {
        var list = new List<TSource>();
        await foreach (var element in source.AsAsyncEnumerable().WithCancellation(cancellationToken).ConfigureAwait(false))
        {
            list.Add(element);
        }

        return list;
    }

    /// <summary>
    ///     Asynchronously creates an array from an <see cref="IQueryable{T}" /> by enumerating it asynchronously.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <typeparam name="TSource">The type of the elements of <paramref name="source" />.</typeparam>
    /// <param name="source">An <see cref="IQueryable{T}" /> to create an array from.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains an array that contains elements from the input sequence.
    /// </returns>
    /// <exception cref="ArgumentNullException"><paramref name="source" /> is <see langword="null" />.</exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static async Task<TSource[]> ToArrayAsync<TSource>(
        this IQueryable<TSource> source,
        CancellationToken cancellationToken = default)
        => (await source.ToListAsync(cancellationToken).ConfigureAwait(false)).ToArray();

    #endregion

    #region ToHashSet

    /// <summary>
    ///     Asynchronously creates a <see cref="HashSet{T}" /> from an <see cref="IQueryable{T}" /> by enumerating it
    ///     asynchronously.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <typeparam name="TSource">The type of the elements of <paramref name="source" />.</typeparam>
    /// <param name="source">An <see cref="IQueryable{T}" /> to create a set from.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains a <see cref="HashSet{T}" /> that contains elements from the input sequence.
    /// </returns>
    /// <exception cref="ArgumentNullException"><paramref name="source" /> is <see langword="null" />.</exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static async Task<HashSet<TSource>> ToHashSetAsync<TSource>(
        this IQueryable<TSource> source,
        CancellationToken cancellationToken = default)
    {
        var set = new HashSet<TSource>();
        await foreach (var element in source.AsAsyncEnumerable().WithCancellation(cancellationToken).ConfigureAwait(false))
        {
            set.Add(element);
        }

        return set;
    }

    /// <summary>
    ///     Asynchronously creates a <see cref="HashSet{T}" /> from an <see cref="IQueryable{T}" /> by enumerating it
    ///     asynchronously.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <typeparam name="TSource">The type of the elements of <paramref name="source" />.</typeparam>
    /// <param name="source">An <see cref="IQueryable{T}" /> to create a set from.</param>
    /// <param name="comparer">
    ///     The <see cref="IEqualityComparer{T}" /> implementation to use when comparing values in the set, or null to use the
    ///     default <see cref="EqualityComparer{T}" /> implementation for the set type.
    /// </param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains a <see cref="HashSet{T}" /> that contains elements from the input sequence.
    /// </returns>
    /// <exception cref="ArgumentNullException"><paramref name="source" /> is <see langword="null" />.</exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static async Task<HashSet<TSource>> ToHashSetAsync<TSource>(
        this IQueryable<TSource> source,
        IEqualityComparer<TSource>? comparer,
        CancellationToken cancellationToken = default)
    {
        var set = new HashSet<TSource>(comparer);
        await foreach (var element in source.AsAsyncEnumerable().WithCancellation(cancellationToken).ConfigureAwait(false))
        {
            set.Add(element);
        }

        return set;
    }

    #endregion

    #region Include

    internal static readonly MethodInfo IncludeMethodInfo
        = typeof(EntityFrameworkQueryableExtensions)
            .GetTypeInfo().GetDeclaredMethods(nameof(Include))
            .Single(mi =>
                mi.GetGenericArguments().Length == 2
                && mi.GetParameters().Any(pi => pi.Name == "navigationPropertyPath" && pi.ParameterType != typeof(string)));

    internal static readonly MethodInfo NotQuiteIncludeMethodInfo
        = typeof(EntityFrameworkQueryableExtensions)
            .GetTypeInfo().GetDeclaredMethods(nameof(NotQuiteInclude))
            .Single(mi =>
                mi.GetGenericArguments().Length == 2
                && mi.GetParameters().Any(pi => pi.Name == "navigationPropertyPath" && pi.ParameterType != typeof(string)));

    /// <summary>
    ///     Specifies related entities to include in the query results. The navigation property to be included is specified starting with the
    ///     type of entity being queried (<typeparamref name="TEntity" />). If you wish to include additional types based on the navigation
    ///     properties of the type being included, then chain a call to
    ///     <see
    ///         cref="ThenInclude{TEntity, TPreviousProperty, TProperty}(IIncludableQueryable{TEntity, IEnumerable{TPreviousProperty}}, Expression{Func{TPreviousProperty, TProperty}})" />
    ///     after this call.
    /// </summary>
    /// <remarks>
    ///     See <see href="https://aka.ms/efcore-docs-load-related-data">Loading related entities</see> for more information
    ///     and examples.
    /// </remarks>
    /// <typeparam name="TEntity">The type of entity being queried.</typeparam>
    /// <typeparam name="TProperty">The type of the related entity to be included.</typeparam>
    /// <param name="source">The source query.</param>
    /// <param name="navigationPropertyPath">
    ///     A lambda expression representing the navigation property to be included (<c>t => t.Property1</c>).
    /// </param>
    /// <returns>A new query with the related data included.</returns>
    /// <exception cref="ArgumentNullException">
    ///     <paramref name="source" /> or <paramref name="navigationPropertyPath" /> is <see langword="null" />.
    /// </exception>
    public static IIncludableQueryable<TEntity, TProperty> Include<TEntity, TProperty>(
        this IQueryable<TEntity> source,
        Expression<Func<TEntity, TProperty>> navigationPropertyPath)
        where TEntity : class
    {
        Check.NotNull(navigationPropertyPath);

        return new IncludableQueryable<TEntity, TProperty>(
            source.Provider is EntityQueryProvider
                ? source.Provider.CreateQuery<TEntity>(
                    Expression.Call(
                        instance: null,
                        method: IncludeMethodInfo.MakeGenericMethod(typeof(TEntity), typeof(TProperty)),
                        arguments: [source.Expression, Expression.Quote(navigationPropertyPath)]))
                : source);
    }

    // A version of Include that doesn't set the navigation as loaded
    internal static IIncludableQueryable<TEntity, TProperty> NotQuiteInclude<TEntity, TProperty>(
        this IQueryable<TEntity> source,
        Expression<Func<TEntity, TProperty>> navigationPropertyPath)
        where TEntity : class
        => new IncludableQueryable<TEntity, TProperty>(
            source.Provider is EntityQueryProvider
                ? source.Provider.CreateQuery<TEntity>(
                    Expression.Call(
                        instance: null,
                        method: NotQuiteIncludeMethodInfo.MakeGenericMethod(typeof(TEntity), typeof(TProperty)),
                        arguments: [source.Expression, Expression.Quote(navigationPropertyPath)]))
                : source);

    internal static readonly MethodInfo ThenIncludeAfterEnumerableMethodInfo
        = typeof(EntityFrameworkQueryableExtensions)
            .GetTypeInfo().GetDeclaredMethods(nameof(ThenInclude))
            .Where(mi => mi.GetGenericArguments().Length == 3)
            .Single(mi =>
            {
                var typeInfo = mi.GetParameters()[0].ParameterType.GenericTypeArguments[1];
                return typeInfo.IsGenericType
                    && typeInfo.GetGenericTypeDefinition() == typeof(IEnumerable<>);
            });

    internal static readonly MethodInfo ThenIncludeAfterReferenceMethodInfo
        = typeof(EntityFrameworkQueryableExtensions)
            .GetTypeInfo().GetDeclaredMethods(nameof(ThenInclude))
            .Single(mi => mi.GetGenericArguments().Length == 3
                && mi.GetParameters()[0].ParameterType.GenericTypeArguments[1].IsGenericParameter);

    /// <summary>
    ///     Specifies additional related data to be further included based on a related type that was just included.
    /// </summary>
    /// <remarks>
    ///     See <see href="https://aka.ms/efcore-docs-load-related-data">Loading related entities</see> for more information
    ///     and examples.
    /// </remarks>
    /// <typeparam name="TEntity">The type of entity being queried.</typeparam>
    /// <typeparam name="TPreviousProperty">The type of the entity that was just included.</typeparam>
    /// <typeparam name="TProperty">The type of the related entity to be included.</typeparam>
    /// <param name="source">The source query.</param>
    /// <param name="navigationPropertyPath">
    ///     A lambda expression representing the navigation property to be included (<c>t => t.Property1</c>).
    /// </param>
    /// <returns>A new query with the related data included.</returns>
    public static IIncludableQueryable<TEntity, TProperty> ThenInclude<TEntity, TPreviousProperty, TProperty>(
        this IIncludableQueryable<TEntity, IEnumerable<TPreviousProperty>> source,
        Expression<Func<TPreviousProperty, TProperty>> navigationPropertyPath)
        where TEntity : class
        => new IncludableQueryable<TEntity, TProperty>(
            source.Provider is EntityQueryProvider
                ? source.Provider.CreateQuery<TEntity>(
                    Expression.Call(
                        instance: null,
                        method: ThenIncludeAfterEnumerableMethodInfo.MakeGenericMethod(
                            typeof(TEntity), typeof(TPreviousProperty), typeof(TProperty)),
                        arguments: [source.Expression, Expression.Quote(navigationPropertyPath)]))
                : source);

    /// <summary>
    ///     Specifies additional related data to be further included based on a related type that was just included.
    /// </summary>
    /// <remarks>
    ///     See <see href="https://aka.ms/efcore-docs-load-related-data">Loading related entities</see> for more information
    ///     and examples.
    /// </remarks>
    /// <typeparam name="TEntity">The type of entity being queried.</typeparam>
    /// <typeparam name="TPreviousProperty">The type of the entity that was just included.</typeparam>
    /// <typeparam name="TProperty">The type of the related entity to be included.</typeparam>
    /// <param name="source">The source query.</param>
    /// <param name="navigationPropertyPath">
    ///     A lambda expression representing the navigation property to be included (<c>t => t.Property1</c>).
    /// </param>
    /// <returns>A new query with the related data included.</returns>
    public static IIncludableQueryable<TEntity, TProperty> ThenInclude<TEntity, TPreviousProperty, TProperty>(
        this IIncludableQueryable<TEntity, TPreviousProperty> source,
        Expression<Func<TPreviousProperty, TProperty>> navigationPropertyPath)
        where TEntity : class
        => new IncludableQueryable<TEntity, TProperty>(
            source.Provider is EntityQueryProvider
                ? source.Provider.CreateQuery<TEntity>(
                    Expression.Call(
                        instance: null,
                        method: ThenIncludeAfterReferenceMethodInfo.MakeGenericMethod(
                            typeof(TEntity), typeof(TPreviousProperty), typeof(TProperty)),
                        arguments: [source.Expression, Expression.Quote(navigationPropertyPath)]))
                : source);

    private sealed class IncludableQueryable<TEntity, TProperty>(IQueryable<TEntity> queryable)
        : IIncludableQueryable<TEntity, TProperty>, IAsyncEnumerable<TEntity>
    {
        public Expression Expression
            => queryable.Expression;

        public Type ElementType
            => queryable.ElementType;

        public IQueryProvider Provider
            => queryable.Provider;

        public IAsyncEnumerator<TEntity> GetAsyncEnumerator(CancellationToken cancellationToken = default)
            => ((IAsyncEnumerable<TEntity>)queryable).GetAsyncEnumerator(cancellationToken);

        public IEnumerator<TEntity> GetEnumerator()
            => queryable.GetEnumerator();

        IEnumerator IEnumerable.GetEnumerator()
            => GetEnumerator();
    }

    internal static readonly MethodInfo StringIncludeMethodInfo
        = typeof(EntityFrameworkQueryableExtensions)
            .GetTypeInfo().GetDeclaredMethods(nameof(Include))
            .Single(mi => mi.GetParameters().Any(pi => pi.Name == "navigationPropertyPath" && pi.ParameterType == typeof(string)));

    /// <summary>
    ///     Specifies related entities to include in the query results. The navigation property to be included is
    ///     specified starting with the type of entity being queried (<typeparamref name="TEntity" />). Further
    ///     navigation properties to be included can be appended, separated by the '.' character.
    /// </summary>
    /// <remarks>
    ///     See <see href="https://aka.ms/efcore-docs-load-related-data">Loading related entities</see> for more information
    ///     and examples.
    /// </remarks>
    /// <typeparam name="TEntity">The type of entity being queried.</typeparam>
    /// <param name="source">The source query.</param>
    /// <param name="navigationPropertyPath">A string of '.' separated navigation property names to be included.</param>
    /// <returns>A new query with the related data included.</returns>
    /// <exception cref="ArgumentNullException">
    ///     <paramref name="source" /> or <paramref name="navigationPropertyPath" /> is <see langword="null" />.
    /// </exception>
    /// <exception cref="ArgumentException"><paramref name="navigationPropertyPath" /> is empty or whitespace.</exception>
    public static IQueryable<TEntity> Include<TEntity>(
        this IQueryable<TEntity> source,
        [NotParameterized] string navigationPropertyPath)
        where TEntity : class
    {
        Check.NotEmpty(navigationPropertyPath);

        return
            source.Provider is EntityQueryProvider
                ? source.Provider.CreateQuery<TEntity>(
                    Expression.Call(
                        instance: null,
                        method: StringIncludeMethodInfo.MakeGenericMethod(typeof(TEntity)),
                        arg0: source.Expression,
                        arg1: Expression.Constant(navigationPropertyPath)))
                : source;
    }

    #endregion

    #region Auto included navigations

    internal static readonly MethodInfo IgnoreAutoIncludesMethodInfo
        = typeof(EntityFrameworkQueryableExtensions).GetTypeInfo().GetDeclaredMethod(nameof(IgnoreAutoIncludes))!;

    /// <summary>
    ///     Specifies that the current Entity Framework LINQ query should not have any model-level eager loaded navigations applied.
    /// </summary>
    /// <remarks>
    ///     See <see href="https://aka.ms/efcore-docs-load-related-data">Loading related entities</see> for more information and examples.
    /// </remarks>
    /// <typeparam name="TEntity">The type of entity being queried.</typeparam>
    /// <param name="source">The source query.</param>
    /// <returns>A new query that will not apply any model-level eager loaded navigations.</returns>
    public static IQueryable<TEntity> IgnoreAutoIncludes<TEntity>(
        this IQueryable<TEntity> source)
        where TEntity : class
        => source.Provider is EntityQueryProvider
            ? source.Provider.CreateQuery<TEntity>(
                Expression.Call(
                    instance: null,
                    method: IgnoreAutoIncludesMethodInfo.MakeGenericMethod(typeof(TEntity)),
                    arguments: source.Expression))
            : source;

    #endregion

    #region Query Filters

    internal static readonly MethodInfo IgnoreQueryFiltersMethodInfo
        = typeof(EntityFrameworkQueryableExtensions).GetTypeInfo().GetDeclaredMethods(nameof(IgnoreQueryFilters))
            .Where(info => info.GetParameters().Length == 1)
            .First();

    internal static readonly MethodInfo IgnoreNamedQueryFiltersMethodInfo
        = typeof(EntityFrameworkQueryableExtensions).GetTypeInfo().GetDeclaredMethods(nameof(IgnoreQueryFilters))
            .Where(info => info.GetParameters().Length == 2)
            .First();

    /// <summary>
    ///     Specifies that the current Entity Framework LINQ query should not have any model-level entity query filters applied.
    /// </summary>
    /// <remarks>
    ///     See <see href="https://aka.ms/efcore-docs-query-filters">EF Core query filters</see> for more information and examples.
    /// </remarks>
    /// <typeparam name="TEntity">The type of entity being queried.</typeparam>
    /// <param name="source">The source query.</param>
    /// <returns>A new query that will not apply any model-level entity query filters.</returns>
    /// <exception cref="ArgumentNullException"><paramref name="source" /> is <see langword="null" />.</exception>
    public static IQueryable<TEntity> IgnoreQueryFilters<TEntity>(
        this IQueryable<TEntity> source)
        where TEntity : class
        => source.Provider is EntityQueryProvider
            ? source.Provider.CreateQuery<TEntity>(
                Expression.Call(
                    instance: null,
                    method: IgnoreQueryFiltersMethodInfo.MakeGenericMethod(typeof(TEntity)),
                    arguments: source.Expression))
            : source;

    /// <summary>
    ///     Specifies that the current Entity Framework LINQ query should not have any model-level entity query filters applied.
    /// </summary>
    /// <remarks>
    ///     See <see href="https://aka.ms/efcore-docs-query-filters">EF Core query filters</see> for more information and examples.
    /// </remarks>
    /// <typeparam name="TEntity">The type of entity being queried.</typeparam>
    /// <param name="source">The source query.</param>
    /// <param name="filterKeys">The filter keys.</param>
    /// <returns>A new query that will not apply any model-level entity query filters.</returns>
    /// <exception cref="ArgumentNullException"><paramref name="source" /> is <see langword="null" />.</exception>
    public static IQueryable<TEntity> IgnoreQueryFilters<TEntity>(
        this IQueryable<TEntity> source,
        [NotParameterized] IReadOnlyCollection<string> filterKeys)
        where TEntity : class
        => source.Provider is EntityQueryProvider
            ? source.Provider.CreateQuery<TEntity>(
                Expression.Call(
                    instance: null,
                    method: IgnoreNamedQueryFiltersMethodInfo.MakeGenericMethod(typeof(TEntity)),
                    // converting the collection to an array if it isn't already one to ensure consistent caching. Fixes #37112.
                    // #37212 may be a possible future solution providing broader capabilities around parameterizing collections.
                    arguments: [source.Expression, Expression.Constant(filterKeys is string[] ? filterKeys : filterKeys.ToArray())]))
            : source;

    #endregion

    #region Tracking

    internal static readonly MethodInfo AsNoTrackingMethodInfo
        = typeof(EntityFrameworkQueryableExtensions).GetTypeInfo().GetDeclaredMethod(nameof(AsNoTracking))!;

    /// <summary>
    ///     The change tracker will not track any of the entities that are returned from a LINQ query. If the
    ///     entity instances are modified, this will not be detected by the change tracker and
    ///     <see cref="DbContext.SaveChanges()" /> will not persist those changes to the database.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Disabling change tracking is useful for read-only scenarios because it avoids the overhead of setting
    ///         up change tracking for each entity instance. You should not disable change tracking if you want to
    ///         manipulate entity instances and persist those changes to the database using
    ///         <see cref="DbContext.SaveChanges()" />.
    ///     </para>
    ///     <para>
    ///         Identity resolution will not be performed. If an entity with a given key is in different result in the result set
    ///         then they will be different instances.
    ///     </para>
    ///     <para>
    ///         The default tracking behavior for queries can be controlled by <see cref="ChangeTracker.QueryTrackingBehavior" />.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-query-tracking">No-tracking queries in EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <typeparam name="TEntity">The type of entity being queried.</typeparam>
    /// <param name="source">The source query.</param>
    /// <returns>A new query where the result set will not be tracked by the context.</returns>
    /// <exception cref="ArgumentNullException"><paramref name="source" /> is <see langword="null" />.</exception>
    public static IQueryable<TEntity> AsNoTracking<TEntity>(
        this IQueryable<TEntity> source)
        where TEntity : class
        => source.Provider is EntityQueryProvider
            ? source.Provider.CreateQuery<TEntity>(
                Expression.Call(
                    instance: null,
                    method: AsNoTrackingMethodInfo.MakeGenericMethod(typeof(TEntity)),
                    arguments: source.Expression))
            : source;

    internal static readonly MethodInfo AsNoTrackingWithIdentityResolutionMethodInfo
        = typeof(EntityFrameworkQueryableExtensions).GetTypeInfo().GetDeclaredMethod(nameof(AsNoTrackingWithIdentityResolution))!;

    /// <summary>
    ///     The change tracker will not track any of the entities that are returned from a LINQ query. If the
    ///     entity instances are modified, this will not be detected by the change tracker and
    ///     <see cref="DbContext.SaveChanges()" /> will not persist those changes to the database.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Disabling change tracking is useful for read-only scenarios because it avoids the overhead of setting
    ///         up change tracking for each entity instance. You should not disable change tracking if you want to
    ///         manipulate entity instances and persist those changes to the database using
    ///         <see cref="DbContext.SaveChanges()" />.
    ///     </para>
    ///     <para>
    ///         Identity resolution will be performed to ensure that all occurrences of an entity with a given key
    ///         in the result set are represented by the same entity instance.
    ///     </para>
    ///     <para>
    ///         The default tracking behavior for queries can be controlled by <see cref="ChangeTracker.QueryTrackingBehavior" />.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-query-tracking">No-tracking queries in EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <typeparam name="TEntity">The type of entity being queried.</typeparam>
    /// <param name="source">The source query.</param>
    /// <returns>A new query where the result set will not be tracked by the context.</returns>
    /// <exception cref="ArgumentNullException"><paramref name="source" /> is <see langword="null" />.</exception>
    public static IQueryable<TEntity> AsNoTrackingWithIdentityResolution<TEntity>(
        this IQueryable<TEntity> source)
        where TEntity : class
        => source.Provider is EntityQueryProvider
            ? source.Provider.CreateQuery<TEntity>(
                Expression.Call(
                    instance: null,
                    method: AsNoTrackingWithIdentityResolutionMethodInfo.MakeGenericMethod(typeof(TEntity)),
                    arguments: source.Expression))
            : source;

    internal static readonly MethodInfo AsTrackingMethodInfo
        = typeof(EntityFrameworkQueryableExtensions)
            .GetTypeInfo()
            .GetDeclaredMethods(nameof(AsTracking))
            .Single(m => m.GetParameters().Length == 1);

    /// <summary>
    ///     Returns a new query where the change tracker will keep track of changes for all entities that are returned.
    ///     Any modification to the entity instances will be detected and persisted to the database during
    ///     <see cref="DbContext.SaveChanges()" />.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         The default tracking behavior for queries can be controlled by <see cref="ChangeTracker.QueryTrackingBehavior" />.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-query-tracking">Tracking queries in EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <typeparam name="TEntity">The type of entity being queried.</typeparam>
    /// <param name="source">The source query.</param>
    /// <returns>A new query where the result set will be tracked by the context.</returns>
    /// <exception cref="ArgumentNullException"><paramref name="source" /> is <see langword="null" />.</exception>
    public static IQueryable<TEntity> AsTracking<TEntity>(
        this IQueryable<TEntity> source)
        where TEntity : class
        => source.Provider is EntityQueryProvider
            ? source.Provider.CreateQuery<TEntity>(
                Expression.Call(
                    instance: null,
                    method: AsTrackingMethodInfo.MakeGenericMethod(typeof(TEntity)),
                    arguments: source.Expression))
            : source;

    /// <summary>
    ///     Returns a new query where the change tracker will either keep track of changes or not for all entities
    ///     that are returned, depending on the value of the 'track' parameter. When tracking, Any modification
    ///     to the entity instances will be detected and persisted to the database during
    ///     <see cref="DbContext.SaveChanges()" />. When not tracking, if the entity instances are modified, this will
    ///     not be detected by the change tracker and <see cref="DbContext.SaveChanges()" /> will not persist those
    ///     changes to the database.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Disabling change tracking is useful for read-only scenarios because it avoids the overhead of setting
    ///         up change tracking for each entity instance. You should not disable change tracking if you want to
    ///         manipulate entity instances and persist those changes to the database using
    ///         <see cref="DbContext.SaveChanges()" />.
    ///     </para>
    ///     <para>
    ///         The default tracking behavior for queries can be controlled by <see cref="ChangeTracker.QueryTrackingBehavior" />.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-query-tracking">Tracking queries in EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <typeparam name="TEntity">The type of entity being queried.</typeparam>
    /// <param name="source">The source query.</param>
    /// <param name="track">Indicates whether the query will track results or not.</param>
    /// <returns>A new query where the result set will be tracked by the context.</returns>
    /// <exception cref="ArgumentNullException"><paramref name="source" /> is <see langword="null" />.</exception>
    public static IQueryable<TEntity> AsTracking<TEntity>(
        this IQueryable<TEntity> source,
        QueryTrackingBehavior track)
        where TEntity : class
        => track switch
        {
            QueryTrackingBehavior.TrackAll => source.AsTracking(),
            QueryTrackingBehavior.NoTracking => source.AsNoTracking(),
            QueryTrackingBehavior.NoTrackingWithIdentityResolution => source.AsNoTrackingWithIdentityResolution(),
            _ => throw new ArgumentOutOfRangeException(nameof(track))
        };

    #endregion

    #region Tagging

    internal static readonly MethodInfo TagWithMethodInfo
        = typeof(EntityFrameworkQueryableExtensions).GetMethod(
            nameof(TagWith), [typeof(IQueryable<>).MakeGenericType(Type.MakeGenericMethodParameter(0)), typeof(string)])!;

    internal static readonly MethodInfo TagWithCallSiteMethodInfo
        = typeof(EntityFrameworkQueryableExtensions)
            .GetMethod(
                nameof(TagWithCallSite),
                [typeof(IQueryable<>).MakeGenericType(Type.MakeGenericMethodParameter(0)), typeof(string), typeof(int)])!;

    /// <summary>
    ///     Adds a tag to the collection of tags associated with an EF LINQ query. Tags are query annotations
    ///     that can provide contextual tracing information at different points in the query pipeline.
    /// </summary>
    /// <remarks>
    ///     See <see href="https://aka.ms/efcore-docs-query-tags">Tagging queries in EF Core</see> for more information and examples.
    /// </remarks>
    /// <typeparam name="T">The type of entity being queried.</typeparam>
    /// <param name="source">The source query.</param>
    /// <param name="tag">The tag.</param>
    /// <returns>A new query annotated with the given tag.</returns>
    /// <exception cref="ArgumentNullException">
    ///     <paramref name="source" /> or <paramref name="tag" /> is <see langword="null" />.
    /// </exception>
    /// <exception cref="ArgumentException"><paramref name="tag" /> is empty or whitespace.</exception>
    public static IQueryable<T> TagWith<T>(
        this IQueryable<T> source,
        [NotParameterized] string tag)
    {
        Check.NotEmpty(tag);

        return
            source.Provider is EntityQueryProvider
                ? source.Provider.CreateQuery<T>(
                    Expression.Call(
                        instance: null,
                        method: TagWithMethodInfo.MakeGenericMethod(typeof(T)),
                        arg0: source.Expression,
                        arg1: Expression.Constant(tag)))
                : source;
    }

    /// <summary>
    ///     Adds a tag to the collection of tags associated with an EF LINQ query with source file name and line
    ///     where method was called that can provide contextual tracing information at different points in the query pipeline.
    /// </summary>
    /// <remarks>
    ///     See <see href="https://aka.ms/efcore-docs-query-tags">Tagging queries in EF Core</see> for more information and examples.
    /// </remarks>
    /// <typeparam name="T">The type of entity being queried.</typeparam>
    /// <param name="source">The source query.</param>
    /// <param name="filePath">The file name where the method was called</param>
    /// <param name="lineNumber">The file line number where the method was called</param>
    /// <returns>A new query annotated with the given tag.</returns>
    /// <exception cref="ArgumentNullException">
    ///     <paramref name="source" />
    /// </exception>
    public static IQueryable<T> TagWithCallSite<T>(
        this IQueryable<T> source,
        [NotParameterized, CallerFilePath] string? filePath = null,
        [NotParameterized, CallerLineNumber] int lineNumber = 0)
        => source.Provider is EntityQueryProvider
            ? source.Provider.CreateQuery<T>(
                Expression.Call(
                    instance: null,
                    method: TagWithCallSiteMethodInfo.MakeGenericMethod(typeof(T)),
                    arg0: source.Expression,
                    arg1: Expression.Constant(filePath),
                    arg2: Expression.Constant(lineNumber)))
            : source;

    #endregion

    #region Load

    /// <summary>
    ///     Enumerates the query. When using Entity Framework, this causes the results of the query to
    ///     be loaded into the associated context. This is equivalent to calling ToList
    ///     and then throwing away the list (without the overhead of actually creating the list).
    /// </summary>
    /// <remarks>
    ///     See <see href="https://aka.ms/efcore-docs-query">LINQ queries in EF Core</see> for more information and examples.
    /// </remarks>
    /// <param name="source">The source query.</param>
    /// <exception cref="ArgumentNullException"><paramref name="source" /> is <see langword="null" />.</exception>
    public static void Load<TSource>(this IQueryable<TSource> source)
    {
        using var enumerator = source.GetEnumerator();
        while (enumerator.MoveNext())
        {
        }
    }

    /// <summary>
    ///     Asynchronously enumerates the query. When using Entity Framework, this causes the results of the query to
    ///     be loaded into the associated context. This is equivalent to calling ToList
    ///     and then throwing away the list (without the overhead of actually creating the list).
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <param name="source">The source query.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>A task that represents the asynchronous operation.</returns>
    /// <exception cref="ArgumentNullException"><paramref name="source" /> is <see langword="null" />.</exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static async Task LoadAsync<TSource>(
        this IQueryable<TSource> source,
        CancellationToken cancellationToken = default)
    {
        var enumerator = source.AsAsyncEnumerable().GetAsyncEnumerator(cancellationToken);
        await using var _ = enumerator.ConfigureAwait(false);

        while (await enumerator.MoveNextAsync().ConfigureAwait(false))
        {
        }
    }

    #endregion

    #region ToDictionary

    /// <summary>
    ///     Creates a <see cref="Dictionary{TKey, TValue}" /> from an <see cref="IQueryable{T}" /> by enumerating it
    ///     asynchronously
    ///     according to a specified key selector function.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <typeparam name="TSource">The type of the elements of <paramref name="source" />.</typeparam>
    /// <typeparam name="TKey">The type of the key returned by <paramref name="keySelector" />.</typeparam>
    /// <param name="source">An <see cref="IQueryable{T}" /> to create a <see cref="Dictionary{TKey, TValue}" /> from.</param>
    /// <param name="keySelector">A function to extract a key from each element.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains a <see cref="Dictionary{TKey, TSource}" /> that contains selected keys and values.
    /// </returns>
    /// <exception cref="ArgumentNullException">
    ///     <paramref name="source" /> or <paramref name="keySelector" /> is <see langword="null" />.
    /// </exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static Task<Dictionary<TKey, TSource>> ToDictionaryAsync<TSource, TKey>(
        this IQueryable<TSource> source,
        Func<TSource, TKey> keySelector,
        CancellationToken cancellationToken = default)
        where TKey : notnull
        => ToDictionaryAsync(source, keySelector, e => e, comparer: null, cancellationToken);

    /// <summary>
    ///     Creates a <see cref="Dictionary{TKey, TValue}" /> from an <see cref="IQueryable{T}" /> by enumerating it
    ///     asynchronously
    ///     according to a specified key selector function and a comparer.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <typeparam name="TSource">The type of the elements of <paramref name="source" />.</typeparam>
    /// <typeparam name="TKey">The type of the key returned by <paramref name="keySelector" />.</typeparam>
    /// <param name="source">An <see cref="IQueryable{T}" /> to create a <see cref="Dictionary{TKey, TValue}" /> from.</param>
    /// <param name="keySelector">A function to extract a key from each element.</param>
    /// <param name="comparer">An <see cref="IEqualityComparer{TKey}" /> to compare keys.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains a <see cref="Dictionary{TKey, TSource}" /> that contains selected keys and values.
    /// </returns>
    /// <exception cref="ArgumentNullException">
    ///     <paramref name="source" /> or <paramref name="keySelector" /> is <see langword="null" />.
    /// </exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static Task<Dictionary<TKey, TSource>> ToDictionaryAsync<TSource, TKey>(
        this IQueryable<TSource> source,
        Func<TSource, TKey> keySelector,
        IEqualityComparer<TKey> comparer,
        CancellationToken cancellationToken = default)
        where TKey : notnull
        => ToDictionaryAsync(source, keySelector, e => e, comparer, cancellationToken);

    /// <summary>
    ///     Creates a <see cref="Dictionary{TKey, TValue}" /> from an <see cref="IQueryable{T}" /> by enumerating it
    ///     asynchronously
    ///     according to a specified key selector and an element selector function.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <typeparam name="TSource">The type of the elements of <paramref name="source" />.</typeparam>
    /// <typeparam name="TKey">The type of the key returned by <paramref name="keySelector" />.</typeparam>
    /// <typeparam name="TElement">The type of the value returned by <paramref name="elementSelector" />.</typeparam>
    /// <param name="source">An <see cref="IQueryable{T}" /> to create a <see cref="Dictionary{TKey, TValue}" /> from.</param>
    /// <param name="keySelector">A function to extract a key from each element.</param>
    /// <param name="elementSelector">A transform function to produce a result element value from each element.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains a <see cref="Dictionary{TKey, TElement}" /> that contains values of type
    ///     <typeparamref name="TElement" /> selected from the input sequence.
    /// </returns>
    /// <exception cref="ArgumentNullException">
    ///     <paramref name="source" /> or <paramref name="keySelector" /> or <paramref name="elementSelector" /> is <see langword="null" />.
    /// </exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static Task<Dictionary<TKey, TElement>> ToDictionaryAsync<TSource, TKey, TElement>(
        this IQueryable<TSource> source,
        Func<TSource, TKey> keySelector,
        Func<TSource, TElement> elementSelector,
        CancellationToken cancellationToken = default)
        where TKey : notnull
        => ToDictionaryAsync(source, keySelector, elementSelector, comparer: null, cancellationToken);

    /// <summary>
    ///     Creates a <see cref="Dictionary{TKey, TValue}" /> from an <see cref="IQueryable{T}" /> by enumerating it
    ///     asynchronously
    ///     according to a specified key selector function, a comparer, and an element selector function.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <typeparam name="TSource">The type of the elements of <paramref name="source" />.</typeparam>
    /// <typeparam name="TKey">The type of the key returned by <paramref name="keySelector" />.</typeparam>
    /// <typeparam name="TElement">The type of the value returned by <paramref name="elementSelector" />.</typeparam>
    /// <param name="source">An <see cref="IQueryable{T}" /> to create a <see cref="Dictionary{TKey, TValue}" /> from.</param>
    /// <param name="keySelector">A function to extract a key from each element.</param>
    /// <param name="elementSelector">A transform function to produce a result element value from each element.</param>
    /// <param name="comparer">An <see cref="IEqualityComparer{TKey}" /> to compare keys.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>
    ///     A task that represents the asynchronous operation.
    ///     The task result contains a <see cref="Dictionary{TKey, TElement}" /> that contains values of type
    ///     <typeparamref name="TElement" /> selected from the input sequence.
    /// </returns>
    /// <exception cref="ArgumentNullException">
    ///     <paramref name="source" /> or <paramref name="keySelector" /> or <paramref name="elementSelector" /> is <see langword="null" />.
    /// </exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static async Task<Dictionary<TKey, TElement>> ToDictionaryAsync<TSource, TKey, TElement>(
        this IQueryable<TSource> source,
        Func<TSource, TKey> keySelector,
        Func<TSource, TElement> elementSelector,
        IEqualityComparer<TKey>? comparer,
        CancellationToken cancellationToken = default)
        where TKey : notnull
    {
        Check.NotNull(keySelector);
        Check.NotNull(elementSelector);

        var d = new Dictionary<TKey, TElement>(comparer);
        await foreach (var element in source.AsAsyncEnumerable().WithCancellation(cancellationToken).ConfigureAwait(false))
        {
            d.Add(keySelector(element), elementSelector(element));
        }

        return d;
    }

    #endregion

    #region ForEach

    /// <summary>
    ///     Asynchronously enumerates the query results and performs the specified action on each element.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <typeparam name="T">The type of the elements of <paramref name="source" />.</typeparam>
    /// <param name="source">An <see cref="IQueryable{T}" /> to enumerate.</param>
    /// <param name="action">The action to perform on each element.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>A task that represents the asynchronous operation.</returns>
    /// <exception cref="ArgumentNullException">
    ///     <paramref name="source" /> or <paramref name="action" /> is <see langword="null" />.
    /// </exception>
    /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
    public static async Task ForEachAsync<T>(
        this IQueryable<T> source,
        Action<T> action,
        CancellationToken cancellationToken = default)
    {
        Check.NotNull(action);

        await foreach (var element in source.AsAsyncEnumerable().WithCancellation(cancellationToken).ConfigureAwait(false))
        {
            action(element);
        }
    }

    #endregion

    #region AsAsyncEnumerable

    /// <summary>
    ///     Returns an <see cref="IAsyncEnumerable{T}" /> which can be enumerated asynchronously.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         Multiple active operations on the same context instance are not supported. Use <see langword="await" /> to ensure
    ///         that any asynchronous operations have completed before calling another method on this context.
    ///         See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information and examples.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-async-linq">Querying data with EF Core</see> for more information and examples.
    ///     </para>
    /// </remarks>
    /// <typeparam name="TSource">The type of the elements of <paramref name="source" />.</typeparam>
    /// <param name="source">An <see cref="IQueryable{T}" /> to enumerate.</param>
    /// <returns>The query results.</returns>
    /// <exception cref="InvalidOperationException"><paramref name="source" /> is <see langword="null" />.</exception>
    /// <exception cref="ArgumentNullException"><paramref name="source" /> is not a <see cref="IAsyncEnumerable{T}" />.</exception>
    public static IAsyncEnumerable<TSource> AsAsyncEnumerable<TSource>(
        this IQueryable<TSource> source)
    {
        if (source is IAsyncEnumerable<TSource> asyncEnumerable)
        {
            return asyncEnumerable;
        }

        throw new InvalidOperationException(CoreStrings.IQueryableNotAsync(typeof(TSource)));
    }

    #endregion

    #region Impl.

    private static TResult ExecuteAsync<TSource, TResult>(
        MethodInfo operatorMethodInfo,
        IQueryable<TSource> source,
        Expression? expression,
        CancellationToken cancellationToken = default)
    {
        if (source.Provider is IAsyncQueryProvider provider)
        {
            if (operatorMethodInfo.IsGenericMethod)
            {
                operatorMethodInfo
                    = operatorMethodInfo.GetGenericArguments().Length == 2
                        ? operatorMethodInfo.MakeGenericMethod(typeof(TSource), typeof(TResult).GetGenericArguments().Single())
                        : operatorMethodInfo.MakeGenericMethod(typeof(TSource));
            }

            return provider.ExecuteAsync<TResult>(
                Expression.Call(
                    instance: null,
                    method: operatorMethodInfo,
                    arguments: expression == null
                        ? [source.Expression]
                        : [source.Expression, expression]),
                cancellationToken);
        }

        throw new InvalidOperationException(CoreStrings.IQueryableProviderNotAsync);
    }

    private static TResult ExecuteAsync<TSource, TResult>(
        MethodInfo operatorMethodInfo,
        IQueryable<TSource> source,
        LambdaExpression expression,
        CancellationToken cancellationToken = default)
        => ExecuteAsync<TSource, TResult>(
            operatorMethodInfo, source, Expression.Quote(expression), cancellationToken);

    private static TResult ExecuteAsync<TSource, TResult>(
        MethodInfo operatorMethodInfo,
        IQueryable<TSource> source,
        CancellationToken cancellationToken = default)
        => ExecuteAsync<TSource, TResult>(
            operatorMethodInfo, source, (Expression?)null, cancellationToken);

    #endregion

    #region ExecuteDelete

    /// <summary>
    ///     Deletes all database rows for the entity instances which match the LINQ query from the database.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         This operation executes immediately against the database, rather than being deferred until
    ///         <see cref="DbContext.SaveChanges()" /> is called. It also does not interact with the EF change tracker in any way:
    ///         entity instances which happen to be tracked when this operation is invoked aren't taken into account, and aren't updated
    ///         to reflect the changes.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-bulk-operations">Executing bulk operations with EF Core</see>
    ///         for more information and examples.
    ///     </para>
    /// </remarks>
    /// <param name="source">The source query.</param>
    /// <returns>The total number of rows deleted in the database.</returns>
    public static int ExecuteDelete<TSource>(this IQueryable<TSource> source)
        => source.Provider.Execute<int>(Expression.Call(ExecuteDeleteMethodInfo.MakeGenericMethod(typeof(TSource)), source.Expression));

    /// <summary>
    ///     Asynchronously deletes database rows for the entity instances which match the LINQ query from the database.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         This operation executes immediately against the database, rather than being deferred until
    ///         <see cref="DbContext.SaveChanges()" /> is called. It also does not interact with the EF change tracker in any way:
    ///         entity instances which happen to be tracked when this operation is invoked aren't taken into account, and aren't updated
    ///         to reflect the changes.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-bulk-operations">Executing bulk operations with EF Core</see>
    ///         for more information and examples.
    ///     </para>
    /// </remarks>
    /// <param name="source">The source query.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>The total number of rows deleted in the database.</returns>
    public static Task<int> ExecuteDeleteAsync<TSource>(this IQueryable<TSource> source, CancellationToken cancellationToken = default)
        => source.Provider is IAsyncQueryProvider provider
            ? provider.ExecuteAsync<Task<int>>(
                Expression.Call(ExecuteDeleteMethodInfo.MakeGenericMethod(typeof(TSource)), source.Expression), cancellationToken)
            : throw new InvalidOperationException(CoreStrings.IQueryableProviderNotAsync);

    internal static readonly MethodInfo ExecuteDeleteMethodInfo
        = typeof(EntityFrameworkQueryableExtensions).GetTypeInfo().GetDeclaredMethod(nameof(ExecuteDelete))!;

    #endregion

    #region ExecuteUpdate

    /// <summary>
    ///     Updates all database rows for the entity instances which match the LINQ query from the database.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         This operation executes immediately against the database, rather than being deferred until
    ///         <see cref="DbContext.SaveChanges()" /> is called. It also does not interact with the EF change tracker in any way:
    ///         entity instances which happen to be tracked when this operation is invoked aren't taken into account, and aren't updated
    ///         to reflect the changes.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-bulk-operations">Executing bulk operations with EF Core</see>
    ///         for more information and examples.
    ///     </para>
    /// </remarks>
    /// <param name="source">The source query.</param>
    /// <param name="setPropertyCalls">A collection of set property statements specifying properties to update.</param>
    /// <returns>The total number of rows updated in the database.</returns>
    public static int ExecuteUpdate<TSource>(
        this IQueryable<TSource> source,
        Action<UpdateSettersBuilder<TSource>> setPropertyCalls)
    {
        var setterBuilder = new UpdateSettersBuilder<TSource>();
        setPropertyCalls(setterBuilder);
        var setters = setterBuilder.BuildSettersExpression();

        return source.Provider.Execute<int>(
            Expression.Call(
                ExecuteUpdateMethodInfo.MakeGenericMethod(typeof(TSource)),
                source.Expression,
                setters));
    }

    /// <summary>
    ///     Asynchronously updates database rows for the entity instances which match the LINQ query from the database.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         This operation executes immediately against the database, rather than being deferred until
    ///         <see cref="DbContext.SaveChanges()" /> is called. It also does not interact with the EF change tracker in any way:
    ///         entity instances which happen to be tracked when this operation is invoked aren't taken into account, and aren't updated
    ///         to reflect the changes.
    ///     </para>
    ///     <para>
    ///         See <see href="https://aka.ms/efcore-docs-bulk-operations">Executing bulk operations with EF Core</see>
    ///         for more information and examples.
    ///     </para>
    /// </remarks>
    /// <param name="source">The source query.</param>
    /// <param name="setPropertyCalls">A collection of set property statements specifying properties to update.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
    /// <returns>The total number of rows updated in the database.</returns>
    [DynamicDependency(
        "ExecuteUpdate``1(System.Linq.IQueryable{``0},System.Collections.Generic.IReadOnlyList{System.Runtime.CompilerServices.ITuple})",
        typeof(EntityFrameworkQueryableExtensions))]
    public static Task<int> ExecuteUpdateAsync<TSource>(
        this IQueryable<TSource> source,
        Action<UpdateSettersBuilder<TSource>> setPropertyCalls,
        CancellationToken cancellationToken = default)
    {
        var setterBuilder = new UpdateSettersBuilder<TSource>();
        setPropertyCalls(setterBuilder);
        var setters = setterBuilder.BuildSettersExpression();

        return source.Provider is IAsyncQueryProvider provider
            ? provider.ExecuteAsync<Task<int>>(
                Expression.Call(
                    ExecuteUpdateMethodInfo.MakeGenericMethod(typeof(TSource)),
                    source.Expression,
                    setters),
                cancellationToken)
            : throw new InvalidOperationException(CoreStrings.IQueryableProviderNotAsync);
    }

    private static int ExecuteUpdate<TSource>(this IQueryable<TSource> source, [NotParameterized] IReadOnlyList<ITuple> setters)
        => throw new UnreachableException("Can't call this overload directly");

    /// <summary>
    ///     This is an internal API that supports the Entity Framework Core infrastructure and not subject to
    ///     the same compatibility standards as public APIs. It may be changed or removed without notice in
    ///     any release. You should only use it directly in your code with extreme caution and knowing that
    ///     doing so can result in application failures when updating to a new Entity Framework Core release.
    /// </summary>
    [EntityFrameworkInternal]
    public static readonly MethodInfo ExecuteUpdateMethodInfo
        = typeof(EntityFrameworkQueryableExtensions)
            .GetMethods(BindingFlags.NonPublic | BindingFlags.Static)
            .Single(m => m.Name == nameof(ExecuteUpdate) && m.GetParameters()[1].ParameterType == typeof(IReadOnlyList<ITuple>));

    #endregion
}
